什么是 DataBinding ?
DataBinding 是屬于 Jetpack 的一個(gè)支持庫(kù)恬偷,可以讓你以在布局中聲明屬性的方式替代編碼方式將布局中的 UI 組件和應(yīng)用數(shù)據(jù)進(jìn)行綁定
通常我們讓控件和數(shù)據(jù)交互的方式如下面的代碼,調(diào)用 findViewById()
獲取 TextView
控件并將 viewModel
中的 userName
屬性賦值給它:
findViewById<TextView>(R.id.textView).apply {
text = viewModel.userName
}
用 DataBinding 的方式就能消除上面所必須的代碼,如下所示丛晦,效果和上面的代碼完全相等账嚎,但是簡(jiǎn)潔多了愁溜,注意表達(dá)式被包含在 @{}
中
<TextView
android:text="@{viewmodel.userName}" />
這樣你就不用在 Activity 中調(diào)用 UI 框架去綁定組件再賦值了罐栈,優(yōu)點(diǎn)是更簡(jiǎn)潔易維護(hù)(Jetpack 其它組件都完美支持 DataBinding)咕缎,而且也減少了內(nèi)存泄露和空指針的風(fēng)險(xiǎn)
如何使用 DataBinding 庫(kù) ?
1料扰、創(chuàng)建一個(gè)新項(xiàng)目
略
2凭豪、啟用 DataBinding 并轉(zhuǎn)換布局
build.gradle
android {
...
dataBinding {
enabled true
}
}
轉(zhuǎn)換布局(?+?)
轉(zhuǎn)換完成之后代碼大概是這樣的
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
...
<data>
標(biāo)簽就是我們放布局變量的地方,布局變量用于編寫布局表達(dá)式晒杈,布局表達(dá)式被放置在元素的屬性值中嫂伞,格式是@{布局表達(dá)式}
3、實(shí)現(xiàn)你的第一個(gè)布局表達(dá)式
在 <data>
標(biāo)簽中添加一個(gè) String 類型的布局變量
<data>
<variable name="name" type="String"/>
</data>
在 TextView 控件的屬性中使用此變量拯钻,注意 id 和 variable name 盡量不要沖突
<TextView
android:id="@+id/textView"
android:text="@{name}"
...
此時(shí)帖努,@{name}
就是一個(gè)簡(jiǎn)單的引用布局變量的布局表達(dá)式了
4、修改 Activity 中的代碼調(diào)用
修改
setContentView(R.layout.activity_main)
為
val binding : ActivityMainBinding =
DataBindingUtil.setContentView(this,R.layout.activity_main)
現(xiàn)在粪般,直接為 binding 的屬性賦值即可
binding.name = "Kim John"
運(yùn)行 app 看看效果
5然磷、處理用戶事件
至此,基礎(chǔ)的數(shù)據(jù)展示部分已經(jīng)完成刊驴,但 DataBinding 的強(qiáng)大遠(yuǎn)不止于此,它還可以處理用戶事件以及布局表達(dá)式的調(diào)用寡润,我們創(chuàng)建一個(gè) viewModel 以便演示其功能
MainViewModel
class MainViewModel : ViewModel(){
private val _name = MutableLiveData("Kim John")
val name: LiveData<String> = _name
}
修改布局文件
<data>
<!--<variable name="name" type="String"/>-->
<variable name="viewModel" type="com.skyrin.jetpack_databanding.MainViewModel"/>
</data>
...
<TextView
android:id="@+id/textView"
android:text="@{viewModel.name}"
...
修改 MainActivity
class MainActivity : AppCompatActivity() {
// 懶加載得到 viewModel 對(duì)象
private val viewModel by lazy {
ViewModelProviders.of(this).get(MainViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding : ActivityMainBinding =
DataBindingUtil.setContentView(this,R.layout.activity_main)
binding.viewModel = viewModel
}
}
運(yùn)行 app 看看效果
DataBinding 還可以和 LiveData 完美結(jié)合捆憎,我們可以看到 MainViewModel 中的 name
變量是 LiveData 類型,讓我們更進(jìn)一步梭纹,為 binding
添加 LifecycleOwner 讓它具備數(shù)據(jù)感知能力
override fun onCreate(savedInstanceState: Bundle?) {
...
binding.lifecycleOwner = this
}
為了更好的展示 DataBinding 與 LiveData 的互動(dòng)躲惰,我們?cè)?MainViewModel 增加 likes 屬性
class MainViewModel : ViewModel() {
private val _name = MutableLiveData("Kim John")
private val _likes = MutableLiveData(0)
val name: LiveData<String> = _name
val likes: LiveData<Int> = _likes
fun onLike() {
_likes.value = (likes.value ?: 0) + 1
}
}
然后修改布局變量和表達(dá)式如下
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_name"
android:text="@{viewModel.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="200dp"/>
<ProgressBar
android:id="@+id/pb_likes"
android:max="@{10}"
android:progress="@{viewModel.likes}"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="200dp"
android:layout_height="10dp"
app:layout_constraintStart_toStartOf="@+id/tv_name" app:layout_constraintEnd_toEndOf="@+id/tv_name"
android:layout_marginTop="32dp" app:layout_constraintTop_toBottomOf="@+id/tv_name"/>
<Button
android:id="@+id/btn_like"
android:text="@{Integer.toString(viewModel.likes)}"
android:onClick="@{()->viewModel.onLike()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/pb_likes"
app:layout_constraintEnd_toEndOf="@+id/pb_likes" android:layout_marginTop="32dp"
app:layout_constraintTop_toBottomOf="@+id/pb_likes"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
運(yùn)行 app 并點(diǎn)擊 button,你會(huì)看到 progress 進(jìn)度隨著點(diǎn)擊次數(shù)增加变抽,而我們并沒(méi)有在 MainActivity 增加任何代碼础拨,這一切都由 DataBinding 與 LiveData 配合完成
6、BindingAdapter
@BindingAdapter
是 DataBinding 中非常有用的一個(gè)注解绍载,此外還有 @BindingConversion
诡宗、@BindingMethods
等,這里介紹一下最常用的 BindingAdapter
我們先在 MainViewModel 中添加 popularity 屬性击儡,并和 likes 建立關(guān)聯(lián)
class MainViewModel : ViewModel() {
...
// 聲明 popularity 并通過(guò) Transformations 創(chuàng)建其與 likes 的關(guān)系
val popularity: LiveData<Popularity> = Transformations.map(_likes) {
when {
it > 9 -> Popularity.STAR
it > 5 -> Popularity.POPULAR
else -> Popularity.NORMAL
}
}
}
enum class Popularity {
NORMAL, // 普通
POPULAR, // 受歡迎
STAR // 巨星
}
創(chuàng)建 BindingAdapter.kt塔沃,用于創(chuàng)建屬性并提供自定義算法
@BindingAdapter("app:popularityColor")
fun popularityColor(view: TextView, popularity: Popularity) {
val color = getColorPopularity(view.context,popularity)
view.setTextColor(color)
}
@BindingAdapter("app:popularityText")
fun popularityText(view: TextView, popularity: Popularity) {
view.text = getTextPopularity(popularity)
}
fun getTextPopularity(popularity: Popularity): String? {
return when (popularity) {
Popularity.NORMAL -> "??"
Popularity.POPULAR -> "??"
Popularity.STAR -> "??"
}
}
fun getColorPopularity(context: Context, popularity: Popularity): Int {
return when (popularity) {
Popularity.NORMAL -> ContextCompat.getColor(context, R.color.normal)
Popularity.POPULAR -> ContextCompat.getColor(context, R.color.popular)
Popularity.STAR -> ContextCompat.getColor(context, R.color.star)
}
}
現(xiàn)在,我們可以直接在布局屬性中使用它們
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_name"
android:text="@{viewModel.name}"
app:popularityColor="@{viewModel.popularity}"
...
/>
...
<TextView
android:id="@+id/tv_popularity"
app:popularityText="@{viewModel.popularity}"
...
/>
</androidx.constraintlayout.widget.ConstraintLayout>
運(yùn)行 app阳谍,查看我們新加入的功能
完整示例代碼
https://github.com/realskyrin/jetpack_databanding
總結(jié)
至此蛀柴,你應(yīng)該已經(jīng)了解到 DataBinding 的強(qiáng)大之處了,雖然只是一個(gè)簡(jiǎn)單的可交互界面矫夯,但如果用傳統(tǒng)方式去實(shí)現(xiàn)的話 Activity 中至少有 100 行以上代碼鸽疾,而 DataBinding 讓 Activity 變得十分清爽,此外训貌,DataBinding 與 LiveData制肮、ViewModel 等 Jetpack 組件也是天作之合,Android 官方也非常推薦使用 Jetpack 來(lái)開發(fā) APP,這已經(jīng)形成 Android 開發(fā)的固定套路了弄企,熟練掌握這些套路能讓你多一些時(shí)間享受生活超燃,后續(xù)我會(huì)繼續(xù)更新其它 Jetpack 組件的用法,如果覺(jué)得文章還行的話來(lái)個(gè)素質(zhì)三連吧??
參考
https://developer.android.google.cn/topic/libraries/data-binding/?hl=en
https://codelabs.developers.google.com/codelabs/android-databinding/