轉(zhuǎn)自:http://ranseti.top/article/extensions
本教程介紹如何使用Kotlin Android擴展來改進對Android開發(fā)的支持滥沫。
在本教程中,我們將介紹使用Kotlin Android Extensions插件所需的步驟键俱,從而增強Android的開發(fā)體驗兰绣。
View綁定
背景
每個Android開發(fā)人員都知道findViewById()函數(shù)。 毫無疑問编振,這是一個難以閱讀和支持的潛在錯誤和惡劣代碼的來源缀辩。 雖然有幾個庫可用于解決這個問題,但是這些庫需要為每個暴露的View注釋字段踪央。
Kotlin Android Extensions插件使我們能夠獲得與其中一些庫相同的體驗臀玄,而不必添加任何額外的代碼。
實質(zhì)上畅蹂,這允許以下代碼:
// Using R.layout.activity_main from the 'main' source set
import kotlinx.android.synthetic.main.activity_main.*
class MyActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Instead of findViewById<TextView>(R.id.textView)
textView.setText("Hello, world!")
}
}
使用Kotlin Android擴展
- 配置依賴關(guān)系
在本教程中健无,我們將使用Gradle,但使用IntelliJ IDEA項目結(jié)構(gòu)或Maven也可以完成相同的任務(wù)液斜。 有關(guān)設(shè)置Gradle以使用Kotlin的詳細信息累贤,請參閱使用Gradle叠穆。
Android擴展是IntelliJ IDEA和Android Studio的Kotlin插件的一部分。 你不需要安裝額外的插件畦浓。
您只需在模塊的build.gradle文件中啟用Android擴展Gradle插件即可:
apply plugin: 'kotlin-android-extensions'
- 導(dǎo)入合成屬性
一次性導(dǎo)入特定布局的所有小部件屬性非常方便:
import kotlinx.android.synthetic.main.<layout>.*
因此痹束,如果布局文件名是activity_main.xml,我們會導(dǎo)入kotlinx.android.synthetic.main.activity_main讶请。*祷嘶。
如果我們想調(diào)用View的綜合屬性,我們也應(yīng)該導(dǎo)入kotlinx.android.synthetic.main.activity_main.view夺溢。*论巍。
一旦我們這樣做,我們就可以調(diào)用相應(yīng)的擴展风响,它們是以XML文件中的視圖命名的屬性嘉汰。 例如,對于這個view:
<TextView
android:id="@+id/hello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
將會有一個名為hello的屬性:
activity.hello.text = "Hello World!"
- 實驗?zāi)J?Experimental Mode)
Android擴展插件包含幾個實驗性功能状勤,如LayoutContainer支持和Parcelable實現(xiàn)生成器鞋怀。 這些功能尚未被視為生產(chǎn)準備,所以您需要打開build.gradle中的實驗?zāi)J讲拍苁褂盟鼈儯?/p>
androidExtensions {
experimental = true
}
- LayoutContainer支持
Android擴展插件支持不同類型的容器持搜。 最基本的是Activity密似,F(xiàn)ragment和View,但是通過實現(xiàn)LayoutContainer接口葫盼,你可以把(虛擬的)任何類轉(zhuǎn)換成一個Android Extensions容器残腌,例如:
import kotlinx.android.extensions.LayoutContainer
class ViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer {
fun setup(title: String) {
itemTitle.text = "Hello World!"
}
}
請注意,您需要打開實驗標志才能使用LayoutContainer贫导。
- Flavor支持
Android擴展插件支持Android風格抛猫。 假設(shè)你在build.gradle文件中有一個free的名字:
android {
productFlavors {
free {
versionName "1.0-free"
}
}
}
因此,您可以通過添加以下導(dǎo)入來導(dǎo)入free / res / layout / activity_free.xml布局的所有合成屬性:
import kotlinx.android.synthetic.free.activity_free.*
在實驗?zāi)J街泻⒌疲梢灾付ㄈ魏巫凅w名稱(不僅味道)闺金,例如 freeDebug或freeRelease也可以工作。
- View 緩存(View Caching)
調(diào)用findViewById()可能會很慢钱反,特別是在視圖層次很大的情況下掖看,所以Android擴展嘗試通過在容器中緩存視圖來最小化findViewById()調(diào)用。
默認情況下面哥,Android擴展為在Kotlin中編寫的每個容器(Activity哎壳,F(xiàn)ragment,View或LayoutContainer實現(xiàn))添加一個隱藏緩存函數(shù)和一個存儲字段尚卫。 該方法是相當小归榕,所以它不會增加APK的大小。
在以下示例中吱涉,findViewById()僅調(diào)用一次:
class MyActivity : Activity()
fun MyActivity.a() {
textView.text = "Hidden view"
textView.visibility = View.INVISIBLE
}
但在以下情況下:
fun Activity.b() {
textView.text = "Hidden view"
textView.visibility = View.INVISIBLE
}
我們不知道這個函數(shù)是否只會被我們的來源的活動或簡單的Java活動調(diào)用刹泄。 因此外里,即使前面的示例中的MyActivity實例作為接收者傳遞,我們也不使用緩存特石。
- 更改視圖緩存策略
您可以更改全局或每個容器的緩存策略盅蝗。 這也需要開啟實驗?zāi)J健?/p>
項目全局緩存策略在build.gradle文件中設(shè)置:
androidExtensions {
defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE
}
默認情況下,Android擴展插件使用HashMap作為后備存儲姆蘸,但是您可以切換到SparseArray實現(xiàn)墩莫,或者關(guān)閉緩存。 當您僅使用Android擴展的Parcelable部分時逞敷,后者特別有用狂秦。
另外,你可以使用@ContainerOptions注解一個容器來改變它的緩存策略:
import kotlinx.android.extensions.ContainerOptions
@ContainerOptions(cache = CacheImplementation.NO_CACHE)
class MyActivity : Activity()
fun MyActivity.a() {
// findViewById() will be called twice
textView.text = "Hidden view"
textView.visibility = View.INVISIBLE
}
Parcelable
從Kotlin 1.1.4開始推捐,Android擴展插件提供了Parcelable實現(xiàn)生成器作為實驗性功能裂问。
- 啟用Parcelable支持
如上所述應(yīng)用kotlin-android-extensions Gradle插件并打開實驗標志。
- 如何使用
使用@Parcelize對類進行注釋牛柒,并自動生成一個Parcelable實現(xiàn)堪簿。
import kotlinx.android.parcel.Parcelize
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable
@Parcelize需要在主構(gòu)造函數(shù)中聲明所有序列化的屬性。 Android擴展將在每個屬性上發(fā)出警告皮壁,并在類體中聲明一個后臺字段戴甩。 此外,如果某些主構(gòu)造函數(shù)參數(shù)不是屬性闪彼,則不能應(yīng)用@Parcelize。
如果你的類需要更高級的序列化邏輯协饲,你可以把它寫在一個伴隨類中:
@Parcelize
data class Value(val firstName: String, val lastName: String, val age: Int) : Parcelable {
private companion object : Parceler<User> {
override fun User.write(parcel: Parcel, flags: Int) {
// Custom write implementation
}
override fun create(parcel: Parcel): User {
// Custom read implementation
}
}
}
- 支持的類型
@Parcelize支持多種類型:
原始類型(及其盒裝版本);
對象和枚舉;
字符串畏腕,CharSequence;
例外;
Size,SizeF茉稠,Bundle描馅,IBinder,IInterface而线,F(xiàn)ileDescriptor;
SparseArray铭污,SparseIntArray,SparseLongArray膀篮,SparseBooleanArray;
所有的Serializable(是的嘹狞,Date也支持)和Parcelable實現(xiàn);
所有支持類型的集合:List(映射到ArrayList),Set(映射到LinkedHashSet)誓竿,Map(映射到LinkedHashMap);
還有一些具體的實現(xiàn):ArrayList磅网,LinkedList,SortedSet筷屡,NavigableSet涧偷,HashSet簸喂,LinkedHashSet,TreeSet燎潮,SortedMap喻鳄,NavigableMap,HashMap确封,LinkedHashMap除呵,TreeMap,ConcurrentHashMap;
所有支持類型的數(shù)組;
所有受支持類型的可空版本隅肥。
- 自定義Parcelers
即使你的類型不被直接支持竿奏,你也可以為它編寫一個Parceler映射對象。
class ExternalClass(val value: Int)
object ExternalClassParceler : Parceler<ExternalClass> {
override fun create(parcel: Parcel) = ExternalClass(parcel.readInt())
override fun ExternalClass.write(parcel: Parcel, flags: Int) {
parcel.writeInt(value)
}
}
可以使用@TypeParceler或@WriteWith注釋應(yīng)用外部parcelers :
// Class-local parceler
@Parcelable
@TypeParceler<ExternalClass, ExternalClassParceler>()
class MyClass(val external: ExternalClass)
// Property-local parceler
@Parcelable
class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass)
// Type-local parceler
@Parcelable
class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass)