使用拓展方法+内联函数,干掉模板代码

Tags
#kotlin

使用拓展方法+内联函数前

viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
    }
    override fun onPageSelected(position: Int) {
        val radioButton = rg_tabs.getChildAt(position) as RadioButton
        radioButton.isChecked = true
    }
    override fun onPageScrollStateChanged(state: Int) {
    }
})

使用拓展方法+内联函数后

viewPager.addListener {
    val radioButton = rg_tabs.getChildAt(it) as RadioButton
    radioButton.isChecked = true
}

实现原理

看上面的简化写法,你一定会有两个疑问:
  1. viewPager哪来的addListener方法
  1. 上面的代码是怎么关联到onPageSelected方法的
第一个问题很简单,拓展函数就可以了,我们定义了一个如下的拓展函数
inline fun ViewPager.addListener(
        crossinline onScrollStateChanged: (state: Int) -> Unit = {},
        crossinline onScrolled: (position: Int, positionOffset: Float, positionOffsetPixels: Int) -> Unit = { position, positionOffset, positionOffsetPixels -> },
        crossinline onSelected: (position: Int) -> Unit = {}
) {
    val listener = object : ViewPager.OnPageChangeListener {

        override fun onPageScrollStateChanged(state: Int) {
            onScrollStateChanged(state)
        }

        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            onScrolled(position, positionOffset, positionOffsetPixels)
        }

        override fun onPageSelected(position: Int) {
            onSelected(position)
        }

    }
    addOnPageChangeListener(listener)
}
第二个问题,看上面的拓展函数,我们就知道,其实在调用addListener时,就是创建了一个ViewPager.OnPageChangeListener对象,然后添加到addOnPageChangeListener中,本质上我们最原始的代码没有区别
按照上面的拓展方法,其实我们应该这么写:
viewPager.addListener (onSelected = { position->
    val radioButton = rg_tabs.getChildAt(position) as RadioButton
    radioButton.isChecked = true
})
但是,实际上,上面的代码可以有两个简化
  1. 如果方法的参数的最后一个是lambda表达式,可以不写表达式的参数,将表达式的方法体放到外面去
viewPager.addListener { position->
    val radioButton = rg_tabs.getChildAt(position) as RadioButton
    radioButton.isChecked = true
}
  1. 如果lambda表达式的入参只有一个,入参可以省略,用it代替
vp.addListener {
    val radioButton = rg_tabs.getChildAt(it) as RadioButton
    radioButton.isChecked = true
}
好了,最终简化版完成了,enjoy吧。
其实,这个简化技巧可以用到很多地方,比如网络请求,可以将什么网络请求、线程切换、序列化等等都放在拓展方法内部,将成功的回调方法做成lambda表达式放在参数的最后一位,前面也可以加一个错误回调的lambda表达式参数,但一般情况下可以省略,用默认实现就好,这样代码就可以写成
request.addListener {
    handleResponse(it)
}

© fishyer 2022