写了个demo,点击事件离奇的不生效。html
开始实在是看不出有什么问题,错误代码以下:闭包
findViewById<Button>(R.id.btn).setOnClickListener {
View.OnClickListener {
...
}
}
复制代码
运行不报错,就是不触发OnClickListener中的代码。app
那么正确的写法应该是怎样呢?ide
以下所示:函数
// 写法1:OnClickListener写法
findViewById<Button>(R.id.btn).setOnClickListener(
View.OnClickListener {
...
}
)
复制代码
或者这样:spa
// 写法2:闭包写法
findViewById<Button>(R.id.btn).setOnClickListener {
...
}
复制代码
为何那个错误的写法不报错可是却不能正确运行呢?.net
由于错误的写法其实是在写法2的闭包里面又声明了一个OnClickListener,仅仅声明listener固然不能调用里面的代码了。code
为何setOnClickListener
有两种写法呢?htm
实际上这里setOnClickListener
这个方法有两个重载:对象
// 参数是OnClickListener对象
fun setOnClickListener(l: View.OnClickListener?)
// 参数是闭包
fun setOnClickListener(l: (v: View) -> Unit)
复制代码
由于有两个重载方法,setOnClickListener
才能够有两种写法。
所谓闭包:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即便已经离开了创造它的环境也不例外。
kotlin中的闭包通常称为lambda表达式,详细内容能够查看:
咱们这里只看将函数做为参数传递的用法。
由于闭包这一特性的存在,上面setOnClickListener
才能够不使用传统的OnClickListener
, 下面咱们本身定义一个简单的例子来讲明。
传统写法:
// 声明listener
interface TestListener {
fun test()
}
// 声明使用listener做为参数的方法
private fun setListener(listener: TestListener) {
// 调用listener的test方法
listener.test()
}
// 方法中使用
fun main(){
setListener(object : TestListener {
override fun test() {
Log.i("test_tag", "test Listener")
}
})
}
复制代码
闭包写法:
// 声明方法
private fun setListener(listener: () -> Unit) {
// 调用传入的方法
listener()
}
// 方法中使用
fun main(){
setListener { Log.i("test_tag", "test unit") }
}
复制代码
明显简化了许多,不再须要经过声明接口设置监听了。
须要额外说明将函数做为参数传递使用的几种变种:
带参数的闭包:
// 带参数
private fun setListener(l: (c: Context) -> Unit) {
l(applicationContext)
}
// 使用,只有一个参数时,能够用it代替传入的参数
setListener { Toast.makeText(it, "test param", Toast.LENGTH_SHORT).show() }
复制代码
带参数且有返回值:
private fun setListener(l: (c: Context) -> Boolean) {
val b = l(applicationContext)
Toast.makeText(applicationContext, if (b) "当前时间是偶数" else "当前时间是奇数", Toast.LENGTH_SHORT).show()
}
// 使用,只有一个参数时,能够用it代替传入的参数
setListener {
Toast.makeText(it, "test param", Toast.LENGTH_SHORT).show()
// 当前时间是不是偶数
System.currentTimeMillis() % 2 == 0L
}
复制代码