Android開(kāi)發(fā)DataBinding基礎(chǔ)使用
1.前言
DataBinding, 又名數(shù)據(jù)綁定,是Android開(kāi)發(fā)中非常重要的基礎(chǔ)技術(shù),它可以將UI組件和數(shù)據(jù)模型連接起來(lái),使得在數(shù)據(jù)模型發(fā)生變化時(shí),UI組件自動(dòng)更新,從而節(jié)省了大量的代碼和時(shí)間。
DataBinding的原理是通過(guò)編寫XML布局文件,在其中使用特定的標(biāo)簽和語(yǔ)法,將UI組件和數(shù)據(jù)模型連接起來(lái)。當(dāng)布局文件被加載時(shí),DataBinding會(huì)自動(dòng)生成綁定代碼,從而將UI組件和數(shù)據(jù)模型關(guān)聯(lián)起來(lái)。
通過(guò)學(xué)習(xí)DataBinding基礎(chǔ)知識(shí),可以讓你的代碼速度翻倍,提高開(kāi)發(fā)效率和代碼質(zhì)量。因此,如果你希望在Android開(kāi)發(fā)中獲得更高的成功率和更快的發(fā)展速度,那么請(qǐng)務(wù)必學(xué)習(xí)DataBinding技術(shù),掌握其基礎(chǔ)知識(shí),讓自己成為一名高效率的Android開(kāi)發(fā)者!
那么話不多說(shuō),讓我們直接直奔主題。接下來(lái)我將從實(shí)用性的角度,來(lái)逐一講解DataBinding的基礎(chǔ)使用,文章末尾會(huì)給出示例代碼的鏈接地址,希望能給你帶來(lái)啟發(fā)。
2.準(zhǔn)備工作
2.1 啟用
1.DataBinding啟用
android {
dataBinding {
enabled = true
}
}2.ViewBinding啟用
android {
buildFeatures {
viewBinding true
}
}2.2 快捷方式
在你的布局中找到最外層的布局,將光標(biāo)放在如圖位置。
- Windows 請(qǐng)按快捷鍵
Alt + 回車 - Mac 請(qǐng)按快捷鍵
option + 回車


3.DataBinding綁定
3.1 數(shù)據(jù)類型
通常我們?cè)贒ataBinding中綁定的數(shù)據(jù)類型是ViewModel或者是AndroidViewModel,它倆都是生命周期可感知的,唯一的區(qū)別是AndroidViewModel可以獲取到應(yīng)用的上下文Application。
3.2 數(shù)據(jù)創(chuàng)建
ViewModel的創(chuàng)建通常是通過(guò)ViewModelProvider進(jìn)行創(chuàng)建和獲取。
ViewModelProvider(this).get(Xxx::class.java)
而在ViewModel中,通常使用MutableLiveData作為可變UI響應(yīng)數(shù)據(jù)類型。相比較LiveData而言,它開(kāi)放了修改值的接口,下面是一個(gè)ViewModel的簡(jiǎn)單例子:
class RecyclerViewRefreshState(application: Application) : AndroidViewModel(application) {
val title = MutableLiveData("RecyclerView的刷新和加載更多演示")
val isLoading = MutableLiveData(false)
val sampleData = MutableLiveData<List<SimpleItem>>(arrayListOf())
val loadState = MutableLiveData(LoadState.DEFAULT)
val layoutStatus = MutableLiveData(Status.DEFAULT)
}當(dāng)然了,如果你有一個(gè)LiveData會(huì)隨著一個(gè)或多個(gè)LiveData的變化而變化,這個(gè)時(shí)候你可能就需要使用MediatorLiveData,即合并LiveData。
這里我簡(jiǎn)單利用MediatorLiveData實(shí)現(xiàn)一個(gè)組合的LiveData--CombinedLiveData。
open class CombinedLiveData<T>(vararg liveData: LiveData<*>, block: () -> T) :
MediatorLiveData<T>() {
init {
value = block()
liveData.forEach {
addSource(it) {
val newValue = block()
if (value != newValue) {
value = newValue
}
}
}
}
}
fun <R, T1, T2> combineLiveData(
liveData1: LiveData<T1>,
liveData2: LiveData<T2>,
block: (T1?, T2?) -> R
) = CombinedLiveData(liveData1, liveData2) { block(liveData1.value, liveData2.value) }這個(gè)時(shí)候,我們就可以通過(guò)combineLiveData方法將兩個(gè)LiveData組合起來(lái),形成一個(gè)新的LiveData。下面我簡(jiǎn)單給出一個(gè)示例代碼:
class CombineLiveDataState : DataBindingState() {
val userName = MutableLiveData("小明")
val userAge = MutableLiveData(20)
val userInfo = combineLiveData(userName, userAge) { name, age ->
"${name}今年${age}歲了!"
}
fun onAgeChanged() {
userAge.value = userAge.value?.plus(1)
}
}這里變化了userAge的值后,userInfo也會(huì)隨著一起變化。
3.3 視圖綁定
一般我們使用DataBindingUtil進(jìn)行視圖綁定操作。綁定操作我們可分為:綁定Activity、綁定Fragment和綁定View。
- 綁定Activity
使用DataBindingUtil.setContentView方法進(jìn)行綁定。
fun <DataBinding : ViewDataBinding> bindActivity(
activity: ComponentActivity,
layoutId: Int
): DataBinding = DataBindingUtil.setContentView<DataBinding>(activity, layoutId).apply {
lifecycleOwner = activity
}- 綁定Fragment
使用DataBindingUtil.inflate方法進(jìn)行綁定。
fun <DataBinding : ViewDataBinding> bindFragment(
fragment: Fragment,
inflater: LayoutInflater,
layoutId: Int,
parent: ViewGroup? = null,
attachToParent: Boolean = false
): DataBinding = DataBindingUtil.inflate<DataBinding>(inflater, layoutId, parent, attachToParent).apply {
lifecycleOwner = fragment.viewLifecycleOwner
}- 綁定View
使用DataBindingUtil.bind方法進(jìn)行綁定。
fun <DataBinding : ViewDataBinding> bindView(
view: View,
viewLifecycleOwner: LifecycleOwner,
): DataBinding = DataBindingUtil.bind<DataBinding>(view).apply {
lifecycleOwner = viewLifecycleOwner
}【??特別注意事項(xiàng)???】
DataBinding綁定的時(shí)候,一定要給ViewDataBinding賦值LifecycleOwner, 否則ViewModel中的LiveData發(fā)生數(shù)據(jù)改變后,則不會(huì)通知UI組件進(jìn)行頁(yè)面更新。
3.4 數(shù)據(jù)綁定
對(duì)ViewModel的綁定有兩種寫法。
- 直接使用
ViewDataBinding.variableId = xxx直接賦值。
val mainState = ViewModelProvider(this).get(MainState::class.java) activityMainbinding.state = mainState
- 使用
ViewDataBinding.setVariable(int variableId, @Nullable Object value)進(jìn)行賦值。
val mainState = ViewModelProvider(this).get(MainState::class.java) binding.setVariable(BR.state, mainState)
這兩者的唯一區(qū)別在于,第一種需要知道ViewDataBinding的具體類型,而第二種是ViewDataBinding自身的方法,無(wú)需知道ViewDataBinding的具體類型。
一般來(lái)說(shuō)在框架中使用到泛型未知ViewDataBinding具體類型的時(shí)候,都會(huì)使用第二種方式進(jìn)行綁定,可以說(shuō)第二種方式更通用一些。
4.基礎(chǔ)使用
4.1 點(diǎn)擊事件綁定
1.無(wú)參響應(yīng)函數(shù):
fun onIncrement() {
// 方法體
}android:onClick="@{() -> state.onIncrement()}"2.接口變量響應(yīng)函數(shù)
注意,這里變量的類型應(yīng)該是View.OnClickListener接口。
val onClickDecrement = View.OnClickListener {
// 方法體
}android:onClick="@{state.onClickDecrement}"3.有參響應(yīng)函數(shù)
fun onReset(view: View) {
// 方法體
}// 第一種寫法
android:onClick="@{(view) -> state.onReset(view)}"
// 第二種寫法
android:onClick="@{state::onReset}"4.2 @BindingAdapter自定義屬性
所有注解的功能都是基于XML屬性值為DataBinding表達(dá)式才生效(即@{})
使用@BindingAdapter進(jìn)行控件自定義屬性綁定的時(shí)候,一定要使用 "@{}" 進(jìn)行賦值,這一點(diǎn)非常重要!??!
- 頂級(jí)函數(shù)實(shí)現(xiàn)
// Kotlin拓展函數(shù)式寫法, 推薦使用
@BindingAdapter("customTitle")
fun TextView.setCustomTitle(title: String) {
text = "標(biāo)題1: $title"
}
// 第一個(gè)參數(shù)必須是view的子類
@BindingAdapter("customTitle1")
fun setCustomTitle1(view: TextView, title: String) {
view.text = "標(biāo)題2: $title"
}
// 多個(gè)參數(shù)進(jìn)行綁定,requireAll=true,代表兩個(gè)參數(shù)都設(shè)置了才生效,默認(rèn)是true.
// 如果requireAll為false, 你沒(méi)有填寫的屬性值將為null. 所以需要做非空判斷.
@BindingAdapter(value = ["customTitle", "customSize"], requireAll = true)
fun TextView.setTextContent(title: String, size: Int) {
text = "標(biāo)題3: $title"
textSize = size.toFloat()
}【??特別注意事項(xiàng)???】
很多時(shí)候,很多新手在寫DataBinding的時(shí)候,經(jīng)常會(huì)漏掉"@{}",尤其是用數(shù)字和Boolean類型的值時(shí)。就比如我上面設(shè)置的customSize屬性,類型值是Int型,正確的寫法應(yīng)該是下面這樣:
- 正確的寫法
<TextView
style="@style/TextStyle.Title"
android:layout_marginTop="16dp"
app:customSize="@{25}"
app:customTitle="@{state.title}" />- 常見(jiàn)錯(cuò)誤的寫法
<TextView
style="@style/TextStyle.Title"
android:layout_marginTop="16dp"
app:customSize="25"
app:customTitle="@{state.title}" />上述錯(cuò)誤的寫法,運(yùn)行后編譯器會(huì)報(bào)錯(cuò)AAPT: error: attribute customSize (aka com.xuexiang.databindingsample:customSize) not found.。
所以當(dāng)我們寫DataBinding的時(shí)候,如果出現(xiàn)AAPT: error: attribute xxx (aka com.aa.bb:xxx) not found.,十有八九是你賦值漏掉了"@{}"。
- 單例類+@JvmStatic注解
object TitleAdapter {
@JvmStatic
@BindingAdapter("customTitle2")
fun setCustomTitle2(view: TextView, title: String) {
view.text = "標(biāo)題4: $title"
}
}4.3 @BindingConversion自定義類型轉(zhuǎn)換
作用:在使用DataBinding的時(shí)候,對(duì)屬性值進(jìn)行轉(zhuǎn)換,以匹配對(duì)應(yīng)的屬性。
定義:方法必須為公共靜態(tài)(public static)方法,且有且只能有1個(gè)參數(shù)。
下面我給一個(gè)簡(jiǎn)單的例子:
1.對(duì)于User類,age的類型是Int。
data class User(
val name: String,
val gender: String? = "男",
val age: Int = 10,
val phone: String? = "13124765438",
val address: String? = null
)2.使用@BindingAdapter定義了age的類型卻是String。
@BindingAdapter(value = ["name", "age"], requireAll = true)
fun TextView.setUserInfo(name: String, age: String) {
text = "${name}今年${age}歲"
}3.這時(shí)候使用DataBinding的時(shí)候,??的app:age="@{state.user.age}"會(huì)編譯報(bào)錯(cuò),提示類型不匹配。
<TextView
style="@style/TextStyle.Title"
android:layout_marginTop="16dp"
app:name="@{state.user.name}"
app:age="@{state.user.age}"/>4.這個(gè)時(shí)候,我們就可以使用@BindingConversion自定義類型轉(zhuǎn)換: Int -> String, 這樣??的代碼就不會(huì)編譯出錯(cuò)了。
@BindingConversion fun int2string(integer: Int) = integer.toString()
4.4 @{}中表達(dá)式使用
- 常用運(yùn)算符
- 算術(shù) + - / * %
- 字符串合并 +
- 邏輯 && ||
- 二元 & | ^
- 一元 + - ! ~
- 移位 >> >>> <<
- 比較 == > < >= <=
- 三元 ?:
- Array 訪問(wèn) []
<TextView
android:text="@{@string/app_name + @string/app_name}"/><TextView
android:visibility="@{!state.user.phone.empty ? View.VISIBLE : View.GONE}"/>- 常用轉(zhuǎn)義字符
- 空格: \
- <小于號(hào): \<
- \>大于號(hào): \>
- &與號(hào): \&
<TextView
android:visibility="@{!state.user.phone.empty && state.user.age > 5 ? View.VISIBLE : View.GONE}"/>- 資源使用
@string @color @drawable @dimen @array
<TextView
style="@style/TextStyle.Content"
android:text="@{@string/user_format(state.user.name, state.user.gender)}"
android:textColor="@{@color/toast_error_color}"
android:textSize="@{@dimen/xui_config_size_content_text_phone}" />- 集合
集合不屬于java.lang*下, 需要導(dǎo)入全路徑。集合使用[]進(jìn)行訪問(wèn)。
<data>
<import type="java.util.List"/>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
</data><TextView
android:text="@{`key: key1, value:` + map[`key1`]}" />- 引用類的靜態(tài)方法
kotlin中定義靜態(tài)方法,一定要在方法上加上@JvmStatic,否則將無(wú)法成功引用。
(1) 定義方法
object AppUtils {
@JvmStatic
fun getAppInfo(context: Context?) =
context?.let {
"packageName: ${it.packageName}, \nversionName: ${
it.packageManager.getPackageInfo(
it.packageName,
0
).versionName
}"
}
}(2) 導(dǎo)入方法所在類路徑
<import type="com.xuexiang.databindingsample.utils.AppUtils"/>
(3) 引用方法
<TextView
android:text="@{AppUtils.getAppInfo(context)}"/>- 空值合并運(yùn)算符
空值合并運(yùn)算符 ?? 會(huì)取第一個(gè)不為 null 的值作為返回值。
<TextView
android:text="@{`地址:` + (state.user.address ?? `默認(rèn)地址`)}"/>等價(jià)于
<TextView
android:text="@{state.user.address != null ? state.user.address : `默認(rèn)地址`)}"/>4.5 include 和 ViewStub
在主布局文件中將相應(yīng)的變量傳遞給 include 布局,需使用自定義的 bind 命名空間將變量傳遞給 (include/ViewStub), 從而使兩個(gè)布局文件之間共享同一個(gè)變量。
例如,在include中定義的變量id是:<variable name="user" type="...User"/>, 那么就使用 app:user="@{state.user}" 來(lái)綁定數(shù)據(jù),與variable定義的name保持一致。
- include
<include
android:id="@+id/include_layout"
layout="@layout/include_user_info"
app:user="@{state.user}" /><layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.xuexiang.databindingsample.fragment.basic.model.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
style="@style/TextStyle.Content"
android:userInfo="@{user}" />
</LinearLayout>
</layout>如果你想在頁(yè)面中獲取include引用布局的某個(gè)控件時(shí),你需要給include設(shè)置資源id,然后通過(guò)它去訪問(wèn)引用布局中的控件,就以??的例子為例,如果我想訪問(wèn)布局中的TextView,我們可以這樣寫:
binding?.includeLayout?.tvTitle?.text = "用戶信息"
【??特別注意事項(xiàng)???】
這里需要注意的是,include標(biāo)簽,如果設(shè)置了layout_width和layout_height這兩個(gè)屬性,那么布局就是由include外層設(shè)置的layout屬性生效,內(nèi)層屬性不生效。
如果include標(biāo)簽沒(méi)有設(shè)置layout_width和layout_height這兩個(gè)屬性,那么就是由include引用的布局內(nèi)層設(shè)置的layout屬性生效。
舉個(gè)例子,如果把??設(shè)置的include改成下面這樣:
<include
layout="@layout/include_user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:user="@{state.user}" />那么@layout/include_user_info加載的布局,距離上部的距離就是24dp,而不是16dp。
- ViewStub
<ViewStub
android:id="@+id/user_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout="@layout/viewstub_user_info"
app:info="@{state.user}" /><layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="info"
type="com.xuexiang.databindingsample.fragment.basic.model.User" />
</data>
<TextView
style="@style/TextStyle.Content"
android:userInfo="@{info}" />
</layout>因?yàn)閂iewStub功能是延遲加載引用的布局,當(dāng)我們需要讓其進(jìn)行加載的時(shí)候,我們需要通過(guò)ViewStub的資源id獲取到ViewStub,然后進(jìn)行inflate,示例代碼如下:
binding?.userInfo?.viewStub?.inflate()
最后
以上就是本次DataBinding基礎(chǔ)使用的全部?jī)?nèi)容,后面我還會(huì)分享DataBinding的進(jìn)階使用教程,感興趣的小伙伴可以點(diǎn)擊頭像關(guān)注我哦~
本文的全部源碼我都放在了github上, 感興趣的小伙伴可以下下來(lái)研究和學(xué)習(xí)。
項(xiàng)目地址: https://github.com/xuexiangjys/DataBindingSample
以上就是Android開(kāi)發(fā)DataBinding基礎(chǔ)使用的詳細(xì)內(nèi)容,更多關(guān)于Android DataBinding基礎(chǔ)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Flutter 用自定義轉(zhuǎn)場(chǎng)動(dòng)畫實(shí)現(xiàn)頁(yè)面切換
本篇介紹了 fluro 導(dǎo)航到其他頁(yè)面的自定義轉(zhuǎn)場(chǎng)動(dòng)畫實(shí)現(xiàn),F(xiàn)lutter本身提供了不少預(yù)定義的轉(zhuǎn)場(chǎng)動(dòng)畫,可以通過(guò) transitionBuilder 參數(shù)設(shè)計(jì)多種多樣的轉(zhuǎn)場(chǎng)動(dòng)畫,也可以通過(guò)自定義的 AnimatedWidget實(shí)現(xiàn)個(gè)性化的轉(zhuǎn)場(chǎng)動(dòng)畫效果。2021-06-06
基于Android中手勢(shì)交互的實(shí)現(xiàn)方法
本篇文章是對(duì)Android中手勢(shì)交互的實(shí)現(xiàn)進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
Android 8.0中一些坑以及對(duì)應(yīng)的解決方法
這篇文章主要給大家介紹了關(guān)于Android 8.0中一些坑以及對(duì)應(yīng)的解決方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析
今天小編就為大家分享一篇關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
ffmpeg實(shí)現(xiàn)去水印以及切分視頻demo
這篇文章主要為大家介紹了ffmpeg實(shí)現(xiàn)去水印以及切分視頻demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Android?Studio實(shí)現(xiàn)登錄界面功能
這篇文章主要為大家詳細(xì)介紹了Android?Studio實(shí)現(xiàn)登錄界面功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android開(kāi)發(fā)之RadioGroup的簡(jiǎn)單使用與監(jiān)聽(tīng)示例
這篇文章主要介紹了Android開(kāi)發(fā)之RadioGroup的簡(jiǎn)單使用與監(jiān)聽(tīng),結(jié)合實(shí)例形式分析了Android針對(duì)RadioGroup單選按鈕簡(jiǎn)單實(shí)用技巧,需要的朋友可以參考下2017-07-07
Android開(kāi)發(fā)之PopupWindow實(shí)現(xiàn)彈窗效果
這篇文章主要為大家詳細(xì)介紹了Android開(kāi)發(fā)之PopupWindow實(shí)現(xiàn)彈窗效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09
Android RecyclerView實(shí)現(xiàn)下拉列表功能
這篇文章主要介紹了Android RecyclerView實(shí)現(xiàn)下拉列表功能,下拉展開(kāi)更多選項(xiàng),具有一定的實(shí)用性,感興趣的小伙伴們可以參考一下2016-11-11

