Butter Knife
Butter Knife 是安卓開發(fā)中常用的一種 View 綁定框架,主要用來減少 View 的獲取&強轉(zhuǎn)的樣板代碼。
原生的安卓 Java 代碼中,控件需要自己手動獲取和強制轉(zhuǎn)換盟步。
ListView simpleListView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// ...
View view = findViewById(R.id.simple_list);
simpleListView = (ListView) view;
simpleListView.setAdapter(adapter);
}
如上面代碼所示,至少要經(jīng)歷三個步驟:
- 聲明 View 變量躏结,包含 View 的具體類型(simpleListView 對象却盘,ListView 類型)
- 調(diào)用 findViewById 方法獲取資源的 View 對象
- 將 View 對象強制轉(zhuǎn)換成對應(yīng)類型的 VIew(ListView)
雖然 2-3 步可以簡化成一行代碼,但是經(jīng)歷的步驟一定是分明的媳拴。
但是通過 View 綁定黄橘,這個步驟可以簡化到一步:
@BindView(R.id.simple_list) ListView simpleListView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// ...
simpleListView.setAdapter(adapter);
}
沒有 simpleListView 的賦值過程,當(dāng)然更不會有類型轉(zhuǎn)換代碼屈溉。因為這之間的步驟 Butter Knife 框架幫你做了塞关。你只用告訴它資源 ID 和接收對象即可,也就是 BindView 注解的作用子巾。
Kotlin 是怎么做的
var simpleListView: ListView? = null
simpleListView = findViewById(R.id.simple_list) as ListView
simpleListView?.adapter = adapter
在 Kotlin 中也逃不過這三個步驟帆赢,但是相比 Java 的優(yōu)點是強制轉(zhuǎn)換只需要 as 關(guān)鍵字,手敲幾乎沒不方便的地方线梗。用 Java 的情況下椰于,大家?guī)缀醵际窃诘忍栍疫呏苯诱{(diào)用 findViewById 然后按 <Ctrl + Enter> 快捷鍵讓 IDE 自動糾正代碼這種方式。仪搔。瘾婿。
所以面對這種問題,即便是語法靈活得多的 Kotlin 也沒有太大優(yōu)勢,類型的強制轉(zhuǎn)換總是非常令人厭惡的憋他。而且 Kotlin 原生并不支持 Butter Knife 孩饼。
讓 Kotlin 也用上 View 綁定
讓 Kotlin 用上方便的 View 綁定功能主要是兩種方式,它們都挺簡單:
- 讓 Butter Knife 在 Kotlin 上正常工作
- 選擇原生 Kotlin 所支持的 View 綁定框架
第一種方式:
@BindView(R.id.simple_list) @JvmField var simpleListView: ListView? = null
JvmField 注解讓 Kotlin 實例的字段具有與底層相同的可見性竹挡,即對 Java 是可見的镀娶,當(dāng)然它的前提必須是非私有屬性。這點對于需要屬性注入的情況是必須的揪罕,同時也是 Kotter Knife 原生不能支持 Kotlin 的原因梯码。
例如 Kotlin 中并沒有 “靜態(tài)變量” 這個元素,但是提供了 companion object 來模擬靜態(tài)的調(diào)用方式好啰。但是 companion object 在底層仍然不是靜態(tài)的轩娶,這對于 Java 而言企圖通過靜態(tài)調(diào)用 Kotlin 的 companion object 里邊的內(nèi)容是不行的。
想讓 Kotlin 在底層產(chǎn)生靜態(tài)實例框往,需要這樣做:
class Key(val value: Int) {
companion object {
@JvmField
val COMPARATOR: Comparator<Key> = compareBy<Key> { it.value }
}
}
此時的 Key.COMPARATOR 對于 Java(或者其它 JVM 語言)都是是靜態(tài)的鳄抒,它們在底層采樣同樣的方式儲存。第一種方式之所以能解決也是類似的道理椰弊。
第二種方式:
Kotter Knife 是為 Kotlin 語言所寫的 View 綁定框架许溅,它這樣來使用:
val impleListView: ListView by bindView(R.id.simple_list)
從形式上來看,就是把給屬性加注解換成了對屬性訪問進行委托秉版,委托給 bind* 函數(shù)贤重。如果你不了解什么是委托,請看這里清焕。
注意:Kotlin 中的 by 關(guān)鍵字只是省略了委托中的樣板代碼并蝗,和委托設(shè)計模式的思想是一模一樣的。此處的屬性委托背后的實現(xiàn)也非常簡單秸妥,屬性會被延遲計算滚停,第一次訪問時進行 View 的查找和轉(zhuǎn)換,如果沒有找到則會拋出異常粥惧,異常實例是:
IllegalStateException("View ID $id for '${desc.name}' not found.")
究竟應(yīng)不應(yīng)該因為一個注解問題放棄 Butter Knife键畴?
首先你要明白,畢竟 @JvmField 也是 Kotlin 語言重要部分的元素之一影晓,這個重要的部分就是:和 Java 的交互調(diào)用。所以 Kotlin 并不算是不支持 Butter Knife檩禾,在需要它的時候不用多慮挂签,毫無疑問可以當(dāng)做完全兼容的 Java 類庫使用。
Kotlin Android Extensions
然而說到這里盼产,本文的主角還未介紹過... 因為介紹它的時候就是拋棄上述所有東西的時候饵婆。你可以將它當(dāng)做 Kotlin 官方對安卓開發(fā)提供的加強支持:它包括了 View 綁定,并且是一種更方便的新形式戏售。它就是: Kotlin Android Extensions
給項目模塊的 build.gradle 添加配置:
apply plugin: 'kotlin-android-extensions'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
}
然后侨核,就可以直接使用生成的對象了:
// 不可少的 import
import kotlinx.android.synthetic.main.fragment_main.*
// 省略其它 import
class MyFragment : Fragment {
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 直接使用 ListView 對象
simple_list.adapter = adapter
}
// ...
}
注意:我沒有聲明任何的 ListView 對象草穆,更沒有相應(yīng)的賦值操作,simple_list 是資源 ID搓译。也就是說:我直接將資源 ID 名 simple_list 當(dāng)做該 ListView 的對象實例使用悲柱。
當(dāng)然,上面的 import 是不能少的些己,import 的規(guī)則是:
import kotlinx.android.synthetic.main.<layout>.*
即 import 了相應(yīng)的 layout 豌鸡,就能直接使用里邊具有 id 屬性的 View 實例,將 1-2-3 個步驟全部省略段标,可謂是最方便的形式涯冠。
最后
雖然 Butter Knife 非常優(yōu)秀,但是既然我能更優(yōu)雅的解決問題逼庞,還能減少依賴蛇更,何樂不為。所以:既然你用上了 Kotlin赛糟,那么請丟棄所有的 View 注入框架派任。