在本教程中贩据,我們將介紹使用Kotlin Android Extensions插件所需的步驟疚漆,從而增強(qiáng)Android的開(kāi)發(fā)體驗(yàn)瘾晃。
查看綁定
背景
每個(gè)Android開(kāi)發(fā)者都知道這個(gè)findViewById()
功能爆安。毫無(wú)疑問(wèn)醒串,這是一個(gè)難以閱讀和支持的潛在錯(cuò)誤和惡劣代碼的來(lái)源烦却。雖然有幾個(gè)庫(kù)可用于解決這個(gè)問(wèn)題宠叼,但是這些庫(kù)需要為每個(gè)暴露的注釋字段View
。
Kotlin Android Extensions插件使我們能夠獲得與其中一些庫(kù)相同的體驗(yàn)其爵,而不必添加任何額外的代碼冒冬。
實(shí)質(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!")
}
}
textView
是一個(gè)擴(kuò)展屬性Activity
摩渺,并且它具有與在activity_main.xml
(所以它是TextView
)中聲明的相同的類型简烤。
使用Kotlin Android擴(kuò)展
配置依賴關(guān)系
在本教程中,我們將使用Gradle摇幻,但使用IntelliJ IDEA項(xiàng)目結(jié)構(gòu)或Maven也可以完成相同的任務(wù)横侦。有關(guān)設(shè)置Gradle以使用Kotlin的詳細(xì)信息,請(qǐng)參閱使用Gradle绰姻。
Android擴(kuò)展是IntelliJ IDEA和Android Studio的Kotlin插件的一部分丈咐。你不需要安裝額外的插件。
您只需要在模塊build.gradle
文件中啟用Android擴(kuò)展插件就可以了:
apply plugin: 'kotlin-android-extensions'
導(dǎo)入合成屬性
一次性導(dǎo)入特定布局的所有小部件屬性非常方便:
import kotlinx.android.synthetic.main.<layout>.*
因此龙宏,如果布局文件名是activity_main.xml
棵逊,我們會(huì)導(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)的擴(kuò)展黍特,它們是以XML文件中的視圖命名的屬性蛙讥。例如,對(duì)于這個(gè)觀點(diǎn):
<TextView
android:id="@+id/hello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
將會(huì)有一個(gè)名為hello
:
activity.hello.text = "Hello World!"
實(shí)驗(yàn)?zāi)J?a target="_blank" rel="nofollow">
Android擴(kuò)展插件包含幾個(gè)實(shí)驗(yàn)功能灭衷,如LayoutContainer
支持和Parcelable
實(shí)現(xiàn)生成器次慢。這些功能尚未被視為生產(chǎn)準(zhǔn)備,所以您需要打開(kāi)實(shí)驗(yàn)?zāi)J?code>build.gradle才能使用它們:
androidExtensions {
experimental = true
}
LayoutContainer
支持
Android擴(kuò)展插件支持不同類型的容器翔曲。最基本的有Activity
迫像,Fragment
并且View
,但是你可以通過(guò)實(shí)現(xiàn)轉(zhuǎn)(幾乎)任何類到Android擴(kuò)展容器LayoutContainer
接口瞳遍,例如:
import kotlinx.android.extensions.LayoutContainer
class ViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer {
fun setup(title: String) {
itemTitle.text = "Hello World!"
}
}
請(qǐng)注意闻妓,您需要打開(kāi)實(shí)驗(yàn)標(biāo)志才能使用LayoutContainer
。
味道支持
Android擴(kuò)展插件支持Android風(fēng)格掠械。假設(shè)你有一個(gè)free
在你的build.gradle
文件中命名的風(fēng)味:
android {
productFlavors {
free {
versionName "1.0-free"
}
}
}
因此由缆,您可以free/res/layout/activity_free.xml
通過(guò)添加此導(dǎo)入來(lái)導(dǎo)入布局的所有合成屬性:
import kotlinx.android.synthetic.free.activity_free.*
在實(shí)驗(yàn)?zāi)J街?/a>注祖,您可以指定任何變體名稱(不僅味道),例如freeDebug
或freeRelease
將工作均唉。
查看緩存
調(diào)用findViewById()
可能會(huì)很慢是晨,特別是在視圖層次很大的情況下,所以Android擴(kuò)展嘗試findViewById()
通過(guò)在容器中緩存視圖來(lái)最小化調(diào)用舔箭。
默認(rèn)情況下罩缴,Android的擴(kuò)展增加了一個(gè)隱藏的緩存功能和存儲(chǔ)領(lǐng)域,以每個(gè)容器(Activity
限嫌,Fragment
靴庆,View
或LayoutContainer
寫在科特林實(shí)現(xiàn))时捌。該方法是相當(dāng)小怒医,所以它不會(huì)增加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
}
我們不知道這個(gè)函數(shù)是否只會(huì)被我們的來(lái)源的活動(dòng)或簡(jiǎn)單的Java活動(dòng)調(diào)用稚叹。因?yàn)檫@個(gè)原因,即使MyActivity
前面例子的實(shí)例作為接收者傳遞拿诸,我們也不會(huì)在那里使用緩存扒袖。
更改視圖緩存策略
您可以更改全局或每個(gè)容器的緩存策略。這也需要開(kāi)啟實(shí)驗(yàn)?zāi)J?/a>亩码。
項(xiàng)目全局緩存策略在build.gradle
文件中設(shè)置:
androidExtensions {
defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE
}
默認(rèn)情況下季率,Android擴(kuò)展插件HashMap
用作后備存儲(chǔ),但您可以切換到SparseArray
實(shí)現(xiàn)描沟,或者關(guān)閉緩存飒泻。當(dāng)您僅使用Android擴(kuò)展的Parcelable部分時(shí),后者特別有用吏廉。
另外泞遗,您可以使用@ContainerOptions
更改其緩存策略的容器進(jìn)行注釋:
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開(kāi)始,Android擴(kuò)展插件提供了Parcelable實(shí)現(xiàn)生成器作為實(shí)驗(yàn)性功能席覆。
啟用Parcelable支持
如上所述應(yīng)用kotlin-android-extensions
Gradle插件并打開(kāi)實(shí)驗(yàn)標(biāo)志史辙。
如何使用
用類來(lái)注釋類@Parcelize
,并Parcelable
自動(dòng)生成一個(gè)實(shí)現(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擴(kuò)展將在每個(gè)屬性上發(fā)出警告,并在類體中聲明一個(gè)后臺(tái)字段生巡。另外方库,@Parcelize
如果某些主構(gòu)造函數(shù)參數(shù)不是屬性,則不能應(yīng)用障斋。
如果你的類需要更高級(jí)的序列化邏輯纵潦,你可以把它寫在一個(gè)伴隨類中:
@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
支持多種類型:
- 原始類型(及其盒裝版本);
- 對(duì)象和枚舉;
-
String
徐鹤,CharSequence
; -
Exception
; -
Size
,SizeF
邀层,Bundle
返敬,IBinder
,IInterface
寥院,FileDescriptor
; -
SparseArray
劲赠,SparseIntArray
,SparseLongArray
秸谢,SparseBooleanArray
凛澎, - 所有
Serializable
(是的,Date
也支持)和Parcelable
實(shí)現(xiàn); - 所有支持的類型的集合:(
List
映射到ArrayList
)估蹄,Set
(映射到LinkedHashSet
)塑煎,Map
(映射到LinkedHashMap
);- 也有一些具體的實(shí)現(xiàn)的:
ArrayList
,LinkedList
臭蚁,SortedSet
最铁,NavigableSet
,HashSet
垮兑,LinkedHashSet
冷尉,TreeSet
,SortedMap
系枪,NavigableMap
雀哨,HashMap
,LinkedHashMap
私爷,TreeMap
雾棺,ConcurrentHashMap
;
- 也有一些具體的實(shí)現(xiàn)的:
- 所有支持類型的數(shù)組;
- 所有受支持類型的可空版本。
自定義Parceler
s
即使你的類型不被直接支持当犯,你也可以Parceler
為它寫一個(gè)映射對(duì)象垢村。
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)用:
// 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)