寫了個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
}
複製代碼