概念
DataBind 就是 基于apt技術类早,幫我們生成了一些模板代碼锌唾,這些模板代碼大概解決了如下操作:
- 控件變量的聲明灼伤,類似如下:
@NonNull
public final TextView tv1;//自動解決了類型匹配的問題触徐,不用擔心自己手抖觸發(fā)類型轉換異常了
- 控件的查找賦值,相當于自動幫我們完成了類似如下操作:
tv1 = findViewById(R.id.tv1) //確保了自己腦子卡狐赡,忘記給聲明的變量賦值撞鹉,引發(fā)空指針
- 控制的數(shù)據(jù)填充操作,也就是其本意數(shù)據(jù)綁定,類似自動完成了如下操作:
tv1.setText(user.getName())
讀前須知
- 官網連接:數(shù)據(jù)綁定庫
- 本文只講使用層面的對應解析鸟雏,不涉及原理流程之類享郊,這點將在下一篇完善
- 本文主要基于官網的使用實例,集合kapt生成的相關代碼孝鹊,來吃透用法背后的真實面紗(源碼)
- 本文不涉及配置炊琉,基礎引用等,需要有一定的DataBinding使用經驗惶室,但是只用但是不知道為啥這么用的大佬
按照官網的順序一點一點來
最常見的TexView的text填充
< android:text="@{viewModel.name}" />
對應的賦值模板代碼如下:
//聲明數(shù)據(jù)對應數(shù)據(jù)變量 java.lang.String viewModelName = null; //數(shù)據(jù)變量賦值 viewModelName = viewModel.getName(); //數(shù)據(jù)綁定View androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv1, viewModelName); //TextViewBindingAdapter是DataBinding庫給我們提供的一個數(shù)據(jù)綁定適配器 //這個方法翻譯過來就是接管了android:text的屬性的賦值邏輯温自,也就是當遇到上面寫法時,會通過下面的靜態(tài)方法進行賦值 //官方提供的了很多皇钞,可以參考著進行更多屬性賦值功能擴展悼泌,這絕對是最好的copy對象 @BindingAdapter("android:text") public static void setText(TextView view, CharSequence text) { //下面實現(xiàn)很貼心,對新老內容進行了比較夹界,防止了重復調用view.setText(text);引起的過渡繪制馆里,很值得我們學習哦 final CharSequence oldText = view.getText(); if (text == oldText || (text == null && oldText.length() == 0)) { return; } if (text instanceof Spanned) { if (text.equals(oldText)) { return; // No change in the spans, so don't set anything. } } else if (!haveContentsChanged(text, oldText)) { return; // No content changes, so don't set anything. } view.setText(text); }
帶表達式的
< android:visibility="@{viewModel.age > 10 ? View.VISIBLE : View.GONE}" />
對應的賦值代碼塊如下(上面只是一個簡單的表達式,其實還有很多可柿,但是官方并不太建議在布局寫太復雜的表達式鸠踪,這樣會搞的布局文件很亂,按照Jetpack的整體思路复斥,數(shù)據(jù)的邏輯處理营密,應該的ViewModel中處理,布局里最好是取最終值就好目锭,這里吐槽一點评汰,有些說法是Databind會搞的布局文件很亂,其實是用復雜了而已痢虹,人家官方本意其實指向讓你進行數(shù)據(jù)綁定被去,并不想讓你在布局里做太復雜的數(shù)據(jù)邏輯)
//聲明變量,這個名字很長 int viewModelAgeInt10ViewVISIBLEViewGONE = 0; //變量賦值 viewModelAgeInt10ViewVISIBLEViewGONE = ((viewModelAgeInt10) ? (android.view.View.VISIBLE) : (android.view.View.GONE)); //數(shù)據(jù)綁定 // ?咦奖唯,這個咋沒適配器呢惨缆。因為Databind,針對屬性有對應setXXX方法會默認調用其setXXX方法就好丰捷,無需提供Adapter //那android:text坯墨,也有setText()呀,為啥上面有呢病往,因為官方覺得那個方法太簡單了畅蹂,所以給提供了更優(yōu)化的方法,以達到優(yōu)化目的 //也就是說默認的會調用屬性對應的setXXX方法荣恐,如果有適配器定制的話就調用定制的 this.tv3.setVisibility(viewModelAgeInt10ViewVISIBLEViewGONE);
Null合并運算符
< android:text="@{viewModel.name??viewModel.lastName}" />
對應的賦值代碼塊
//聲明變量 java.lang.String viewModelNameJavaLangObjectNullViewModelLastNameViewModelName = null; //給變量賦值液斜,本質還是用了我們三元運算符累贤,也就是這個就是個語法糖 viewModelNameJavaLangObjectNullViewModelLastNameViewModelName = ((viewModelNameJavaLangObjectNull) ? (viewModelLastName) : (viewModelName)); //給View填充數(shù)據(jù),講過了不啰嗦 androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv2, viewModelNameJavaLangObjectNullViewModelLastNameViewModelName);
視圖引用
< android:text="@{tv3.text}" />
適用兩個組件取值一致的場景
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv3, stringValueOfViewModelAge); androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv4, stringValueOfViewModelAge);
其實本質是兩個組件使用了統(tǒng)一個數(shù)據(jù)變量
集合
<!-- 要手動導入,主語xml里不支持‘<’符號少漆,要用<代替 --> <import type="java.util.List"/> <variable name="listdata" type="List<String>" /> <!-- 使用 --> <TextView android:text="@{viewModel.list[1]}"/>
對應代碼生成的代碼
import java.util.List; java.util.List<java.lang.String> viewModelList = null; java.lang.String viewModelList1 = null; viewModelList = viewModel.getList(); if (viewModelList != null) { // read viewModel.list[1] viewModelList1 = getFromList(viewModelList, 1); } androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView14, viewModelList1); //這個方法是ViewDatabinding基類提供的方法臼膏,與之對應還有其它集合類方法,用于獲取集合中某個索引值用 protected static <T> T getFromList(List<T> list, int index) { if (list == null || index < 0 || index >= list.size()) { return null; } return list.get(index); }
字符串字面量
這個就簡單的說下使用示损,場景上就是咱們的值是在雙引號內的渗磅,里面如果需要字面常量時不能再用雙引號,要用單引號检访;當然如果外層用單引號內層就可以用雙引號了始鱼,總之就是不能同時出現(xiàn)兩個雙引號
<!-- 外單內雙 --> < android:text='@{"寫死的值"}' /> <!-- 外雙內單 --> < android:text="@{map[`firstName`]}" />
//到了編譯后就是用的死值,不會為其生成變量 androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv5, "寫死的值");
資源
主要是資源文件的動態(tài)話
<string name="content">I am %s , age is %d</string> < android:text='@{@string/content(viewModel.name,viewModel.age)}' />
java.lang.String tv6AndroidStringContentViewModelNameViewModelAge = null; //可以看到本質上還是調用了tv6.getResources().getString()的方法 tv6AndroidStringContentViewModelNameViewModelAge = tv6.getResources().getString(R.string.content, viewModelName, viewModelAge); androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv6, tv6AndroidStringContentViewModelNameViewModelAge);
方法引用
事件處理的一種方式脆贵,官方的解釋是:
- 在表達式中医清,您可以引用符合監(jiān)聽器方法簽名的方法。當表達式求值結果為方法引用時卖氨,數(shù)據(jù)綁定會將方法引用和所有者對象封裝到監(jiān)聽器中会烙,并在目標視圖上設置該監(jiān)聽器。如果表達式的求值結果為 null筒捺,則數(shù)據(jù)綁定不會創(chuàng)建監(jiān)聽器柏腻,而是設置 null 監(jiān)聽器。
- 事件可以直接綁定到處理腳本方法系吭,類似于為 Activity 中的方法指定 android:onClick 的方式五嫂。與 View
onClick` 特性相比,一個主要優(yōu)點是表達式在編譯時進行處理肯尺,因此贫导,如果該方法不存在或其簽名不正確,則會收到編譯時錯誤蟆盹。
- 方法引用和監(jiān)聽器綁定之間的主要區(qū)別在于實際監(jiān)聽器實現(xiàn)是在綁定數(shù)據(jù)時創(chuàng)建的,而不是在事件觸發(fā)時創(chuàng)建的闺金。如果您希望在事件發(fā)生時對表達式求值逾滥,則應使用監(jiān)聽器綁定。
好難懂是吧败匹,那我們接下來就根據(jù)栗子和源碼去理解吧
首先定義一個方法引用寨昙,如下
class ListenerHandler { fun tvOnClick(view: View){ Toast.makeText(view.context,"aaaa",Toast.LENGTH_LONG).show() } }
這個啥特點呢就是方法的參數(shù)與返回值必須與對應事件的參數(shù)類型一致,方法名隨意掀亩,對應到官方的一句話就是”引用符合監(jiān)聽器方法簽名的方法“
<variable name="clickHandler" type="org.geekbang.databindingtest.ListenerHandler" />
來個錯誤的示范
fun tvOnClick(view: View) --> fun tvOnClick(context: Context);也就是方法簽名搞錯了舔哪,會咋樣呢
會直接有個紅線,提示錯了槽棍,也就是不和規(guī)范在編譯器就不行了
修正過來的寫法android:onClick="@{clickHandler::tvOnClick}"
那么最終編譯出來的相關代碼是啥呢
//老樣子捉蚤,根據(jù)文件里的東東生成一個變量 android.view.View.OnClickListener clickHandlerTvOnClickAndroidViewViewOnClickListener = null; private OnClickListenerImpl mClickHandlerTvOnClickAndroidViewViewOnClickListener; if (clickHandler != null) { clickHandlerTvOnClickAndroidViewViewOnClickListener = (((mClickHandlerTvOnClickAndroidViewViewOnClickListener == null) ? (mClickHandlerTvOnClickAndroidViewViewOnClickListener = new OnClickListenerImpl()) : mClickHandlerTvOnClickAndroidViewViewOnClickListener).setValue(clickHandler)); }
翻譯下就是抬驴,如果mXXX==null,則new OnClickListenerImpl()缆巧,否則mXXX.setValue(clickHandler)更新下值
先看否則 clickHandler布持,很明顯就是我們在布局文件中的<variable name="clickHandler">
核心還是那個OnClickListenerImpl,看下源碼public static class OnClickListenerImpl implements android.view.View.OnClickListener{ private org.geekbang.databindingtest.ListenerHandler value; public OnClickListenerImpl setValue(org.geekbang.databindingtest.ListenerHandler value) { this.value = value; return value == null ? null : this; } @Override public void onClick(android.view.View arg0) { this.value.tvOnClick(arg0); } }
這個類能解釋好多官方解釋
- 這個類和實例是編譯時就創(chuàng)建陕悬,對應官方的話:方法引用和監(jiān)聽器綁定之間的主要區(qū)別在于實際監(jiān)聽器實現(xiàn)是在綁定數(shù)據(jù)時創(chuàng)建的
- 這個類里持有一個變量org.geekbang.databindingtest.ListenerHandler value题暖,這個變量的類型是我們的自定義的實現(xiàn)的類型,值也很明顯就是我們聲明的那個clickHandler捉超;這里對應官方的話:數(shù)據(jù)綁定會將方法引用和所有者對象封裝到監(jiān)聽器中
- 接口的實現(xiàn)最終調用的是value對應的方法胧卤;這也就能解釋通為啥定義的方法一定要符合監(jiān)聽器的方法簽名了,也就是參數(shù)上要對應好拼岳,從這里我們可以發(fā)現(xiàn)枝誊,其實不一定參數(shù)類型完全一致,只要是事件方法參數(shù)的子類類型就可以了裂问,不過一般設置接口時就會根據(jù)依賴倒置規(guī)則確定了類型上不能再具體了侧啼。
最后肯定是設置值了,這一些列操作編譯時相當于都把代碼給我們寫好了堪簿,至此所謂的方法引用方式綁定事件處理就通了
this.tv1.setOnClickListener(clickHandlerTvOnClickAndroidViewViewOnClickListener);
監(jiān)聽器綁定
也是事件處理的一種方式痊乾,官方的解釋來一波:
- 這些是在事件發(fā)生時進行求值的 lambda 表達式。數(shù)據(jù)綁定始終會創(chuàng)建一個要在視圖上設置的監(jiān)聽器椭更。事件被分派后哪审,監(jiān)聽器會對 lambda 表達式進行求值。
- 監(jiān)聽器綁定是在事件發(fā)生時運行的綁定表達式虑瀑。它們類似于方法引用湿滓,但允許您運行任意數(shù)據(jù)綁定表達式。
- 在方法引用中舌狗,方法的參數(shù)必須與事件監(jiān)聽器的參數(shù)匹配叽奥。在監(jiān)聽器綁定中,只有您的返回值必須與監(jiān)聽器的預期返回值相匹配(預期返回值無效除外)
定義痛侍,聲明朝氓,使用
class ListenerHandler { fun onClickByInfo(view:View,text:CharSequence){ Toast.makeText(view.context,text,Toast.LENGTH_LONG).show() } }
<variable name="clickHandler" type="org.geekbang.databindingtest.ListenerHandler" /> < android:onClick="@{(view)->clickHandler.onClickByInfo(view,viewModel.name)}" />
理一下生成的相關代碼,我們倒著看比較好主届,這里倒著看下
//給tv設置監(jiān)聽,這里的callBack肯定是一個OnClickListener this.tv2.setOnClickListener(mCallback1); // 果不其然赵哲,直接就是 @Nullable private final android.view.View.OnClickListener mCallback1; // 那他賦值是誰呢,看下面,這個有兩個參數(shù)君丁,傳了this->ActivityMainBinding枫夺,還有一個1?,這個1是干啥的。绘闷∠鹋樱〗咸常現(xiàn)在不清楚 mCallback1 = new org.geekbang.databindingtest.generated.callback.OnClickListener(this, 1); //看看這個類的實現(xiàn) package org.geekbang.databindingtest.generated.callback; public final class OnClickListener implements android.view.View.OnClickListener { final Listener mListener; final int mSourceId; public OnClickListener(Listener listener, int sourceId) { mListener = listener; mSourceId = sourceId; } @Override public void onClick(android.view.View callbackArg_0) { //我們點擊按鈕時會調這里,這個的具體實現(xiàn)交給了內部接口實例mListener的_internalCallbackOnClick方法去實現(xiàn)了 mListener._internalCallbackOnClick(mSourceId , callbackArg_0); } public interface Listener { void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0); } } 接下來就看那個接口的實現(xiàn)在哪里毙死,通過開始的賦值也能才到燎潮,實現(xiàn)在ActivityMainBinding,那么就看具體實現(xiàn) public final void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0) { boolean clickHandlerJavaLangObjectNull = false; java.lang.String viewModelName = null; org.geekbang.databindingtest.ListenerHandler clickHandler = mClickHandler; org.geekbang.databindingtest.MainViewModel viewModel = mViewModel; boolean viewModelJavaLangObjectNull = false; clickHandlerJavaLangObjectNull = (clickHandler) != (null); if (clickHandlerJavaLangObjectNull) { viewModelJavaLangObjectNull = (viewModel) != (null); if (viewModelJavaLangObjectNull) { viewModelName = viewModel.getName(); //上面都是變量聲明和檢測扼倘,其實可以看出來就是各種非空邏輯的判斷确封,要確保不出現(xiàn)空指針 //這里最終調用了我們定義的方法 clickHandler.onClickByInfo(callbackArg_0, viewModelName); } } }
從上面源碼看,相比方法引用其實就是換了下寫法再菊,同時支持非簽名參數(shù)了而已爪喘,因為這些代碼也都是編譯時都設置好了。
那么這兩種看都是將原來的onClick的具體實現(xiàn)最終轉給了我們自己寫的業(yè)務塊纠拔,只是方法參數(shù)上監(jiān)聽器比方法引用更加靈活秉剑,這里可以推斷出場景選擇,點擊事件不依賴于數(shù)據(jù)邏輯時用方法引用就好稠诲,如果依賴于數(shù)據(jù)侦鹏,比如我們的Rv的Item里的點擊需要把Item的data帶出去,就可以用監(jiān)聽器引用了臀叙。
可觀察數(shù)據(jù)對象
這個其實在實際中都以LiveData代替了略水,這最終是如何運行的,且看下回分解劝萤。我們就通過一個簡單的LiveData實例看看有啥相關代碼渊涝。
val descriptionInfo = MutableLiveData("簡介")
< android:text="@={viewModel.descriptionInfo}" />
倒著看看相關代碼
//賦值 androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tvDes, viewModelDescriptionInfoGetValue); //相關變量聲明,能看到有兩個變量床嫌,一個是值跨释,一個是LiveData androidx.lifecycle.MutableLiveData<java.lang.String> viewModelDescriptionInfo = null; java.lang.String viewModelDescriptionInfoGetValue = null; //值肯定通過LiveData獲取的 if (viewModelDescriptionInfo != null) { viewModelDescriptionInfoGetValue = viewModelDescriptionInfo.getValue(); } // LiveData通過變量賦值 if (viewModel != null) { // read viewModel.descriptionInfo viewModelDescriptionInfo = viewModel.getDescriptionInfo(); } //賦值后還有個下面的方法,從這里順下去應該能找出是如何響應LiveData變化的 updateLiveDataRegistration(1, viewModelDescriptionInfo); /** * 更新liveData的的注冊器 * 本地字段id * LiveData自己厌处,算是一個observable */ protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) { mInLiveDataRegisterObserver = true; try { //最終給了updateRegistration鳖谈,參數(shù)CREATE_LIVE_DATA_LISTENER,這個方法最其實就是經過關聯(lián)判斷阔涉,合理的去給LiveData去更新下觀察者缆娃,就不展開了 return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER); } finally { mInLiveDataRegisterObserver = false; } } //CREATE_LIVE_DATA_LISTENER是啥?可以看到其本質是LiveDataListener洒敏,是ViewDataBinding的靜態(tài)常量。那就瞅一眼疙驾,有點多凶伙,我們只關注下核心的 //implements Observer是一個觀察者 private static class LiveDataListener implements Observer, ObservableReference<LiveData<?>> { //一個監(jiān)聽相關的數(shù)據(jù)包裝類,將各個參數(shù)存到這里面了 final WeakListener<LiveData<?>> mListener; //略... mListener = new WeakListener(binder, localFieldId, this, referenceQueue); //略... //會給LiveData設置監(jiān)聽 liveData.observe(newOwner, this); //略... //響應監(jiān)聽變化 public void onChanged(@Nullable Object o) { ViewDataBinding binder = mListener.getBinder(); if (binder != null) { binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0); } } }
從上線我們能看到當生性周期狀態(tài)變化后它碎,會交由ViewDataBinding的handleFieldChange去響應變化函荣,接下來我們跟一下這條線的主要代碼
//如果字段變化后會走requestRebind boolean result = onFieldChange(mLocalFieldId, object, fieldId); if (result) { requestRebind(); } //requestRebind是請求重新綁定的意思显押,最終繞來繞去會走到executeBindings();而executeBindings是執(zhí)行綁定的意思,最終會回到我們開始的賦值階段
從上面看傻挂,我們的LiveData數(shù)據(jù)的管理還是有點復雜的乘碑,主要是很多預制的東西,本質上還是注冊觀察者和響應觀察者而已金拒,只是將這一套流程模板化了而已
綁定適配器
自己的理解
- 這個官網講述的都已經很詳細了兽肤,這里主要是做做總結,加深下理解绪抛,沒興趣的可以直接看官網资铡。
- 綁定的實質是設置控件的某個屬性,如setText是改變文字幢码,setAlpha是改變透明度笤休,那么要改變這個屬性可能需要一些邏輯去定制,比如setText症副,如果新舊值沒有變化就沒必要設置了店雅,多刷新一次而已,那么適配器的本質作用就是為設置屬性提供自定義實現(xiàn)方式贞铣,類似于RecycleView中我們將布局填充交給Adapter一樣闹啦,這里是我們將某個屬性的修改交給了一個Adapter而已。
適配器的定制原則
- 理論上任何屬性都可以定制
- 自動選擇:有setter方法的都可以直接搞
- 有屬性有對應的setter方法咕娄,可以直接用亥揖;例如android:gravity="@{vm.gravity}"
- 沒有屬性,但是有setter方法圣勒,可以通過app:xxx="xxx"方式使用费变;例如app:scrimColor="@{@color/scrim}"
- 指定自定義方法名稱:有屬性,但是沒有對應的setter圣贸,但是呢有其它名稱的方法可以單獨修改這個屬性挚歧,可以將屬性及方法建立關聯(lián)即可。例如android:tint這個屬性吁峻,木有setTint方法滑负,但是有setImageTintList方法是用來設置各個屬性的,我們通過一下方法進行關鍵即可用含。示例:
其實這個有矮慕,但是很少,首先一般寫法都是用對應setter方法啄骇,及時需要關聯(lián)的痴鳄,大多數(shù)DataBinding框架已經幫我們建立關聯(lián)了,發(fā)現(xiàn)木有時到androidx.databinding.adapters下找找看缸夹。@BindingMethods(value = [ BindingMethod( type = android.widget.ImageView::class, attribute = "android:tint", method = "setImageTintList")])
- 完全自定義:這個就是既沒有默認痪寻,也無法1v1關聯(lián)的場景了螺句;例如我們有android:paddingLeft屬性,但沒有該屬性的sePaddingLeft,有改變的方法setPadding但這個哥們是四個參數(shù)是改四個padding值用的橡类,也就是完全驢唇對不上馬嘴蛇尚,就只能自定義了,這也是我們大多數(shù)場景了顾画。
- 這個主要是單參數(shù)取劫,組合參數(shù)的情況,官網給了明確的示例亲雪,并且官方庫也提供了很多封裝勇凭,比著葫蘆畫瓢就好。注意在Kotlin中比官方更簡單點义辕,因為Kotlin可以用擴展函數(shù)實現(xiàn)虾标,省了一個參數(shù)的聲明
- @BindingAdapter 其實讀一下這個注解的源碼及注釋就一通百通了
對象轉換
- 我們目標上在綁定數(shù)據(jù)時需要確保與屬性的值類型一致,如我們gravity屬性需要一個int灌砖,我們給一個字符串類型的肯定不合適璧函。但是某些邏輯下我們得到的可能并不是int類型,這就需要轉換了基显。轉換的目的是讓值能符合屬性設置的類型要求蘸吓。
- 自動轉換:有些屬性可以支持多種類型,比如setText撩幽,可以是int的resId库继,可以是String類型,自動轉換的理解就是可以根據(jù)給定的值類型自動選擇調用哪個方法來設置屬性窜醉。
- 自定義轉換:就是原始值不滿足屬性值的類型要求宪萄;舉兩個例子,backGround屬性需要Drawable榨惰,我們給@color/xxx肯性不行拜英,還有一個較為常見的場景,我們一般拿到的時間戳琅催,但是需要顯示的是xxxx年xx月xx日的格式居凶。定義方法比較簡單,就是在方法上加個@BindingConversion就可以了藤抡。咱們用個例子看下
// 我們有一個user對象 val user = User("二胖胖",18)
我們定義個轉義器<!-- 給text直接指定了對象了,理論上我們木有setText(user:User)類型的方法的 --> < android:text="@{viewModel.user}" />
看下最終表現(xiàn)@BindingConversion fun convertUserToString(user: User): String? { return user.toString() }
也就是編譯器會發(fā)現(xiàn)類型不匹配會找對應的covert方法來用androidx.databinding.adapters.TextViewBindingAdapter.setText(this.tv9, org.geekbang.databindingtest.ViewExBindingAdaptersKt.convertUserToString(viewModelUser));
雙向綁定
- 首先這個東西看官網得看幾遍缠黍,個人覺得不是很好理解
- 其實掌握以下就可以了
- 這個的應用場景就是弄兜,某個UI依賴一個數(shù)據(jù)展示,這個UI的內容變化后會改變這個數(shù)值;例如EditText挨队,我們初始值依賴一個變量value,隨著輸入的變化輸入框的值會實時改變value的值蒿往,翻譯過來就是通過簡單的寫法可以實現(xiàn)如下兩個操作:
editText.text = value editText.addTextChangedListener(object : TextWatcher(){ override fun afterTextChanged(s: Editable?) { value = s.toString() } })
- 寫法的話就是語法糖@=
< android:text="@={value}" />
- 系統(tǒng)提供了大多數(shù)使用場景雙向特性
- 想自定義的話盛垦,需要掌握幾個注解用法 @BindingAdapter @InverseBindingAdapter @InverseBindingMethods
- 這個的應用場景就是弄兜,某個UI依賴一個數(shù)據(jù)展示,這個UI的內容變化后會改變這個數(shù)值;例如EditText挨队,我們初始值依賴一個變量value,隨著輸入的變化輸入框的值會實時改變value的值蒿往,翻譯過來就是通過簡單的寫法可以實現(xiàn)如下兩個操作:
ViewStub
- 額...沒咋用過...也就沒深入研究了,但是這個是布局優(yōu)化的一個點瓤漏,有興趣深究下就好
include
- 本質只是一個DataBinding的包裹腾夯,掌握傳值就好了,不過這里寫演示實例時遇到個坑蔬充,就是在約束布局里的寬高用wrapper不行蝶俱,了解下就好了。