https://github.com/LaxusJie/DataBindingSimple
Data binding 入坑筆記二進階篇之雙向綁定
https://www.cnblogs.com/longjunhao/p/5860353.html
https://academy.realm.io/cn/posts/data-binding-android-boyar-mount/ 英文翻譯(較全)
https://www.colabug.com/1197309.html? 使用@InverseBindingMethods實現(xiàn)反向綁定
https://blog.csdn.net/zhangphil/article/details/77839555? 使用@InverseBindingAdapter反向綁定
DataBinding框架使用的的環(huán)境要求:
1寸士、保證接入的項目gradle插件版本不低于1.5.0-alpha1:
classpath'com.android.tools.build:gradle:1.5.0'
2、在對應模塊(Module)的build.gradle文件添加如下配置
dataBinding {??
? ? ?enabledtrue
}
Databinding接入
一惕艳、布局文件
要使用Data Binding首先xml文件有了以下變化,他的根節(jié)點變成了layout尝偎,里面包含一個data節(jié)點和原根節(jié)點LinearLayout凿滤,其中這個data是用來綁定數(shù)據(jù)的拔创。
在data內可以利用variable聲明變量,里面有兩個屬性name和type涌哲,name是變量的名字胖缤,type是變量的類型。(這里的User是一個實體類阀圾,下面說)?
也可以把類型提出來用import導入 哪廓,基礎類型(java.lang包下的,比如String)可以直接使用稍刀,無需import撩独。
配置好布局文件后,IDE會根據(jù)xml文件的名稱自動生成一個DataBinding類用于數(shù)據(jù)綁定账月,命名規(guī)則如下
activity_main.xml-> ActivityMainBinding
如果不喜歡自動生成的Data Binding名综膀,我們可以自己來定義
? ? .........
class對應的就是生成的Data Binding名
二、(viewModle)POJO對象
POJO類對象User局齿,這種類型的對象具有從不改變的數(shù)據(jù)剧劝。在應用程序中,數(shù)據(jù)是一次讀取抓歼,此后從不更改讥此,這是很常見的。
public class User{
? public final String firstName;
? public final String lastName;
? public User(String firstName, String lastName) {
? ? ? this.firstName = firstName;
? ? ? this.lastName = lastName;?
? ? ?}
}
也可以使用JavaBean形式
public class User{
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
?this.firstName = firstName;
?this.lastName = lastName;?
?}
? public String getFirstName() {
? ? ? ?returnthis.firstName;?
?}
? public String getLastName() {
? ?returnthis.lastName;
? }
}
修改Activity中的onCreate方法谣妻,
用DataBindingUtil.setContentView替代原來的setContentView方法從而實現(xiàn)數(shù)據(jù)綁定,創(chuàng)建user對象萄喳,
通過binding.setUser(user)從而實現(xiàn)數(shù)據(jù)填充。(此時還是自動填充蹋半,需要手動調用binding對象的setUser方法通binding對象更新view視圖)
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
? ? ? ?super.onCreate(savedInstanceState);?
?? ? ? ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);? ? ?
? ? ? ?user =newUser("Laxus","J");?
?? ? ? binding.setUser(user);??
? }
此處可以多說一句他巨,binding的setUser()方法是根據(jù)布局文件中標簽在編譯時生成了一個方法,binding也是編譯時生成的一個類减江,這個方法就聲明在binding這個類中染突,并且還有對應的一個getUser方法。除此之外辈灼,binging里還有int age這個屬性的set份企,get方法;
前面說了巡莹,寫到這個一步已經可以將user對象的數(shù)據(jù)刷新到布局視圖中去了司志,但是需要手動調用setUser方法甜紫,那么怎樣才能在數(shù)據(jù)更新后自動更新到布局中而不用調用setUser方法呢?
Databing提供了一個Observable接口俐芯,實現(xiàn)此接口的的pojo類可以被Databing框架識別和管理棵介;為了方便,Databing框架已經為我們封裝了一個BaseObservable類吧史,里面幫我們已經封裝了好了基礎功能;
讓一個類繼承BaseObservable:
public class Student extends BaseObservable{
? private String name;
? privateString className;
? @Bindable
? public String getName() {
? ? return? name;??
? ?}
@Bindable
public String getClassName() {
return className;? ?
?}
public void setName(String name) {
this.name?= name;
notifyPropertyChanged(BR.name);
?}
public void setClassName(String className) {
? ? this.className = className;? ? ?
?? notifyPropertyChanged(BR.className);??
? }
}
BR是編譯階段生成的一個類唠雕,功能與 R.java 類似贸营,用?@Bindable標記過?getter方法會在BR中生成一個靜態(tài)常量池,所有Pojo類的屬性在這個BR都有唯一一個對應的BR.XX常量岩睁。
public class BR{
public? static? final int_all =0;
public? static? final? int className =1
}
在setter方法里我們需要用notifyPropertyChanged方法來更新對象钞脂。寫了這個notifyPropertyChanged方法,此時就可以不用之后再改變一個對象中的某一個屬性捕儒,就可以不用再調用binding對象的整個set方法冰啃;
還有另一種適用于POJO的寫法,這種寫法不需要繼承BaseObservable刘莹,不過每個成員變量都要new一個ObservableField對象阎毅。
public class Teacher{
public final? ?ObservableField name =newObservableField<>();
?public? final? ObservableField className = newObservableField<>();
? public? final? ObservableInt age =newObservableInt();
}
注意,這個類里面沒有set点弯、get方法扇调,不用寫,Databing已經做了處理抢肛,使用時在代碼中如下寫:
mTeacher.name.set("wang");
?mTeacher.className.set("Java");
而在布局文件中的使用方法與普通屬性寫法一樣狼钮;
DataBinding? 還準備了ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble, 以及ObservableParcelableObservableArrayMap和ObservableArrayList。
逆向綁定
上邊已經講完了捡絮,數(shù)據(jù)改變引起視圖改變的單向綁定熬芜,下面說一下頁面中空間的內容改變如何引起數(shù)據(jù)改變;
如果想在用戶輸入的時候給一個pojo類的屬性賦值福稳,則需要用到@={}這個操作符
只比單向綁定時涎拉,在@和{}中間多了一個=,就變成了雙向綁定灵寺!
引申
我們引申兩點曼库,一是逆向綁定的原理和另一個用法,二略板,整個Databing的工作原理毁枯。
逆向綁定:
Databing在對頁面生成一個bing對象時,會根據(jù)這個布局頁面里的view生成一些代碼叮称,其中就包括了如下代碼:
// values
// listeners
// Inverse Binding Event Handlers
private android.databinding.InverseBindingListener mboundView1androidTextAttrChanged =
? ? ? ?new android.databinding.InverseBindingListener() {
? ? ? ? ? @Override
? ? ? ? ?public void onChange() {
// Inverse of?student.name
? ? ? ? ? // is student.setName((java.lang.String) callbackArg_0)
? ? ? ? java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView1);? ? ? ? ? // localize variables for thread safety
? ? ? ? // student com.jie.databindingsimple.entity.Student student = mStudent;
//?student.name?java.lang.String studentName = null;
? ? ? ? ?// student != null
? ? ? ? if (student)!= null)) {
? ? ? ? ?student.setName(((java.lang.String) (callbackArg_0)));
? ? ? ? ?}
? ? ? }
?};
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
? ?public static String getTextString(TextView view) {
? ? ? ? return view.getText().toString();
? ? }
@BindingAdapter(value?=?{"android:textAttrChanged"},?requireAll?=?false)??
?public?static?void?setAndroidTextAttrChanged(Textview textview,?InverseBindingListener?inverseBindingListener)?{?
? ? ? ? ? if?(inverseBindingListener?==?null)?{???
???????????Log.e("錯誤种玛!",?"InverseBindingListener為空!");??????
? ? ? ? ?}?else?{? ? ? ?// 觸發(fā)更新
? ? ? ? ? inverseBindingListener =inverseBindingListener 藐鹤;
? ? ? ?}????
??}??
先是Databing在編譯時根據(jù)布局文件在生成如上代碼,運行時當用戶改變了文本赂韵,觸發(fā)textview的text屬性的改變娱节,然后Databing根據(jù)text屬性找到了getTextString方法和上邊的的注解中的event,然后根據(jù)event中的android:textAttrChanged找到setAndroidTextAttrChanged方法祭示,并將mboundView1androidTextAttrChanged屬性設置進去肄满,而textview的相關監(jiān)聽中判斷InverseBindingListener!=null了质涛,就調用InverseBindingListener.onChange();????onChange方法找到getTextString方法獲取文本并設置給Pojo對象稠歉;以上的的代碼Databing已經幫我們寫好了,不過知道了這幾部原理汇陆,我們也可以定義自己view邏輯怒炸。具體參考:
https://www.colabug.com/1197309.html?使用@InverseBindingMethods實現(xiàn)反向綁定
https://blog.csdn.net/zhangphil/article/details/77839555使用@InverseBindingAdapter反向綁定。
我回頭看一下Observable接口:
package android.databinding;
public interface Observable {
? ? ? void? ? ?addOnPropertyChangedCallback(Observable.OnPropertyChangedCallback var1);
? ? ? void? ? ?removeOnPropertyChangedCallback(Observable.OnPropertyChangedCallback var1);
? ? ?public? abstract static class OnPropertyChangedCallback { public OnPropertyChangedCallback() { }
? ? ?public? ?abstract void onPropertyChanged(Observable var1, int var2); }
}
Observable接口中有一個addOnPropertyChangedCallback方法用來添加一個接收屬性改變的callback毡代,我可以在activity中這樣寫阅羹,
來監(jiān)聽到某個屬性的改變;
ActivityTwowayBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_twoway);
Student student = new Student();
binding.setStudent(student);
student.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable observable, int i) {
switch (i) {
case?BR.name:
? ? Toast.makeText(TwowayActivity.this, "name", Toast.LENGTH_SHORT).show();
? ? break;
? ? case BR.className:
? ? Toast.makeText(TwowayActivity.this, "classname", Toast.LENGTH_SHORT).show();
? ? break;
? ? ? ? }
? ? ?}
? });
}
目前雙向綁定僅支持如text教寂,checked捏鱼,year,month孝宗,hour穷躁,rating,progress等綁定因妇。其它的需要我們自定義问潭;
Databing原理
http://www.reibang.com/p/c41e7a597ac8
自定義用法
1、轉換器
類型轉換對view賦值時有些類型需要轉換婚被,例如:color 為int值 狡忙,
view.setBackground 需要的是drawable參數(shù),這個時候址芯,需要定義類型轉換灾茁,
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
? return new ColorDrawable(color);
}
注意這個方法必須是public static的,該方法可以定義在任何地方谷炸。
2北专、適配器 @BindAdapter
適配器開發(fā)中一個比較常見的場景就是使用ImageLoader進行加載圖片,在任何地方添加適配器代碼:
@BindingAdapter("android:src"旬陡,requireAll = false)
public static void setImageUrl(ImageView view, String url) {?
? ? ? Picasso.with(view.getContext()).load(url).into(view);
}
這個時候可以使用下面代碼進行數(shù)據(jù)綁定:當進行bind的時候拓颓,會自動調用適配器定義的函數(shù)。適配器也可以用來添加view的屬性描孟。比如我想在Textview中綁定long型的時間戳驶睦,并且按照一個格式顯示砰左。
@android.databinding.BindingAdapter(value = {"app:date", "app:format"}, requireAll = false)
public static void bindFormat(TextView view, String date, String format) {
? if (date == null) {
? ? ? return;
? }
SimpleDateFormat formatter;
if (format != null) {
? ? ? formatter = new SimpleDateFormat(format);
? }else {
? ? ? formatter = new SimpleDateFormat("yyyy-MM-dd");
? }
? ? ?Long timeStamp = Long.parseLong(date);
? ? ?view.setText(formatter.format(new Date(timeStamp)));
}
其中 requireAll = false表示當參數(shù)不全時,也會調用適配器函數(shù)场航,補全的參數(shù)會傳入空值缠导,所以要自己判斷。
關于ImageView的另一種用法:
@BindingAdapter(value={"android:src","placeHolder"},requireAll=false)
public static void setImageUrl(ImageViewview,Stringurl,intplaceHolder){
? RequestCreator requestCreator=Picasso.with(view.getContext()).load(url);
? ? ?if(placeHolder!=0){
? ? ? ? ? requestCreator.placeholder(placeHolder);
? ?}?
? ? ? requestCreator.into(view);
}
資源(Resources):
可以使用正常語法的表達式訪問資源:
[html]view plaincopy
android:padding=“@{large??@dimen/largePadding?:?@dimen/smallPadding}”??
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
格式字符串和復數(shù)可通過提供參數(shù)判斷:
[html]view plaincopy
android:text=“@{@string/nameFormat(firstName,?lastName)}”??
android:text=“@{@plurals/banana(bananaCount)}”??
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
當復數(shù)需要多個參數(shù)時溉痢,所有參數(shù)都應通過:
[html]view plaincopy
??Have?an?orange??
??Have?%d?oranges??
android:text=“@{@plurals/orange(orangeCount,?orangeCount)}”??
? Have an orange
? Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
一些資源需要顯示類型判斷:
自動安裝(Automatic Setters)
對于一個屬性僻造,Data Binding 試圖找到 setAttribute 方法,與該屬性的 name space 并沒有什么關系孩饼,僅僅與屬性本身名稱有關
例如嫡意,有關 TextView 的 android:text 屬性的表達式會尋找一個 setText(String) 的方法,如果表達式返回一個 int,Data Binding會搜索的 setText(int) 方法捣辆,這里要注意:表達式要返回正確的類型,如果需要的話使用 casting此迅,Data Binding 任然會工作汽畴,即使沒有給定的名稱屬性存在,然后耸序,你可以通過 Data Binding 輕松地為任何 setter “創(chuàng)造”屬性忍些,例如,DrawerLayout 沒有任何屬性坎怪,但大量的 setters罢坝,你可以使用自動 setters 來使用其中的一個:
[html]view plaincopy
android:layout_width=“wrap_content”? ? ?
android:layout_height=“wrap_content”??
app:scrimColor=“@{@color/scrim}”??
app:drawerListener=“@{fragment.drawerListener}”/>??
? ? android:layout_width="wrap_content"
? ? android:layout_height="wrap_content"
? ? app:scrimColor="@{@color/scrim}"
? ? app:drawerListener="@{fragment.drawerListener}"/>
重命名 Setters(Renamed Setters)
一些有 Setters 的屬性按名稱并不匹配,對于這些方法搅窿,屬性可以通過 BindingMethods 注解相關聯(lián)嘁酿,還必須與一個包含BindingMethod 注解類相關聯(lián),每一個用于重命名的方法男应,例如 ?andorid:tin 屬性與 ?setImageTintList 相關聯(lián)闹司,而不與 setTint相關
[java]view plaincopy
@BindingMethods({??
@BindingMethod(type?=?“android.widget.ImageView”,??
attribute?=”android:tint”,??
method?=”setImageTintList”),??
})??
@BindingMethods({
? ? ? @BindingMethod(type = "android.widget.ImageView",
? ? ? ? ? ? ? ? ? ? ? attribute = "android:tint",
? ? ? ? ? ? ? ? ? ? ? method = "setImageTintList"),
})
上面的例子,開發(fā)者不太可能重命名編譯程序沐飘,Android 框架屬性已經實現(xiàn)了
泛型支持
? ?
? ? ">
">// 左尖括號需要轉義游桩,
自定義 Setters(Custom Setters)
有些屬性需要自定義綁定邏輯,例如耐朴,對于 android:paddingLeft 屬性并沒有相關的setter借卧,相反,setPadding (left筛峭、top铐刘、right、bottom)是存在滨达,一個帶有 BindingAdapter 注解的靜態(tài)綁定適配器方法允許開發(fā)者自定義 setter 如何對于一個屬性的調用
Android 的屬性已經創(chuàng)造了 BindingAdapters,舉例來說,對于 paddingLeft:
[java]view plaincopy
@BindingAdapter(“android:paddingLeft”)??
public?static?void?setPaddingLeft(View?view,?int?padding)?{??
???view.setPadding(padding,??
???????????????????view.getPaddingTop(),??
???????????????????view.getPaddingRight(),??
???????????????????view.getPaddingBottom());??
}??
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
? view.setPadding(padding,
? ? ? ? ? ? ? ? ? view.getPaddingTop(),
? ? ? ? ? ? ? ? ? view.getPaddingRight(),
? ? ? ? ? ? ? ? ? view.getPaddingBottom());
}
Binding 適配對其他定制類型非常有用续挟,例如,自定義 loader 可以用異步載入圖像
當有沖突時博个,開發(fā)人員創(chuàng)建的 Binding 適配器將覆蓋 Data Binding 默認適配器
你也可以創(chuàng)建可以接收多個參數(shù)的適配器:
[java]view plaincopy
@BindingAdapter({“bind:imageUrl”,?“bind:error”})??
public?static?void?loadImage(ImageView?view,?String?url,?Drawable?error)?{??
???Picasso.with(view.getContext()).load(url).error(error).into(view);??
}??
@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
? Picasso.with(view.getContext()).load(url).error(error).into(view);
}
[html]view plaincopy
app:error=“@{@drawable/venueError}”/>??
app:error="@{@drawable/venueError}"/>
如果對于一個 ImageView imageUrl 和 error 都被使用蜒什,并且 imageUrl 是一個 String 類型以及 error 是一個 drawable 時着绊,該適配器被調用
匹配的過程中自定義 name spaces 將被忽略
你也可以為 Android name spaces 寫適配器
綁定適配器方法可以在其處理程序中選擇舊值。取舊值和新值的方法應具有所有屬性的舊值,其次是新值:
[java]view plaincopy
@BindingAdapter(“android:paddingLeft”)??
public?static?void?setPaddingLeft(View?view,?int?oldPadding,?int?newPadding)?{??
if?(oldPadding?!=?newPadding)?{??
???????view.setPadding(newPadding,??
???????????????????????view.getPaddingTop(),??
???????????????????????view.getPaddingRight(),??
???????????????????????view.getPaddingBottom());??
???}??
}??
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
? if (oldPadding != newPadding) {
? ? ? view.setPadding(newPadding,
? ? ? ? ? ? ? ? ? ? ? view.getPaddingTop(),
? ? ? ? ? ? ? ? ? ? ? view.getPaddingRight(),
? ? ? ? ? ? ? ? ? ? ? view.getPaddingBottom());
? }
}
事件處理程序只能用一個抽象方法與接口或抽象類一起使用,例如:
[java]view plaincopy
@BindingAdapter(“android:onLayoutChange”)??
public?static?void?setOnLayoutChangeListener(View?view,?View.OnLayoutChangeListener?oldValue,??
???????View.OnLayoutChangeListener?newValue)?{??
if?(Build.VERSION.SDK_INT?>=?Build.VERSION_CODES.HONEYCOMB)?{??
if?(oldValue?!=?null)?{??
????????????view.removeOnLayoutChangeListener(oldValue);??
????????}??
if?(newValue?!=?null)?{??
????????????view.addOnLayoutChangeListener(newValue);??
????????}??
????}??
}??
@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
? ? ? View.OnLayoutChangeListener newValue) {
? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
? ? ? ? if (oldValue != null) {
? ? ? ? ? ? view.removeOnLayoutChangeListener(oldValue);
? ? ? ? }
? ? ? ? if (newValue != null) {
? ? ? ? ? ? view.addOnLayoutChangeListener(newValue);
? ? ? ? }
? ? }
}
當監(jiān)聽器有多個方法時卜录,它必須被分割成多個監(jiān)聽器,例如嗦篱,View.OnAttachStateChangeListener 的方法有兩種:onViewAttachedToWindow() 和 onViewDetachedFromWindow()涵卵。然后坏晦,我們必須創(chuàng)建兩個接口來區(qū)分它們的屬性和處理程序:
[java]view plaincopy
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)??
public?interface?OnViewDetachedFromWindow?{??
void?onViewDetachedFromWindow(View?v);??
}??
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)??
public?interface?OnViewAttachedToWindow?{??
void?onViewAttachedToWindow(View?v);??
}??
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
? ? void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
? ? void onViewAttachedToWindow(View v);
}
因為改變一個監(jiān)聽器也會影響另一個,我們必須有三個不同的綁定適配器,一個為每個屬性和一個為兩者拾积,他們應該被設置:
[java]view plaincopy
@BindingAdapter(“android:onViewAttachedToWindow”)??
public?static?void?setListener(View?view,?OnViewAttachedToWindow?attached)?{??
setListener(view,null,?attached);??
}??
@BindingAdapter(“android:onViewDetachedFromWindow”)??
public?static?void?setListener(View?view,?OnViewDetachedFromWindow?detached)?{??
setListener(view,?detached,null);??
}??
@BindingAdapter({“android:onViewDetachedFromWindow”,?“android:onViewAttachedToWindow”})??
public?static?void?setListener(View?view,?final?OnViewDetachedFromWindow?detach,??
final?OnViewAttachedToWindow?attach)?{??
if?(VERSION.SDK_INT?>=?VERSION_CODES.HONEYCOMB_MR1)?{??
final?OnAttachStateChangeListener?newListener;??
if?(detach?==?null?&&?attach?==?null)?{??
newListener?=null;??
}else?{??
newListener?=new?OnAttachStateChangeListener()?{??
@Override??
public?void?onViewAttachedToWindow(View?v)?{??
if?(attach?!=?null)?{??
????????????????????????attach.onViewAttachedToWindow(v);??
????????????????????}??
????????????????}??
@Override??
public?void?onViewDetachedFromWindow(View?v)?{??
if?(detach?!=?null)?{??
????????????????????????detach.onViewDetachedFromWindow(v);??
????????????????????}??
????????????????}??
????????????};??
????????}??
final?OnAttachStateChangeListener?oldListener?=?ListenerUtil.trackListener(view,??
newListener,R.id.onAttachStateChangeListener);
if?(oldListener?!=?null)?{??
????????????view.removeOnAttachStateChangeListener(oldListener);??
????????}??
if?(newListener?!=?null)?{??
????????????view.addOnAttachStateChangeListener(newListener);??
????????}??
????}??
}??
@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
? ? setListener(view, null, attached);
}
@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
? ? setListener(view, detached, null);
}
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
? ? ? ? final OnViewAttachedToWindow attach) {
? ? if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
? ? ? ? final OnAttachStateChangeListener newListener;
? ? ? ? if (detach == null && attach == null) {
? ? ? ? ? ? newListener = null;
? ? ? ? } else {
? ? ? ? ? ? newListener = new OnAttachStateChangeListener() {
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void onViewAttachedToWindow(View v) {
? ? ? ? ? ? ? ? ? ? if (attach != null) {
? ? ? ? ? ? ? ? ? ? ? ? attach.onViewAttachedToWindow(v);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? public void onViewDetachedFromWindow(View v) {
? ? ? ? ? ? ? ? ? ? if (detach != null) {
? ? ? ? ? ? ? ? ? ? ? ? detach.onViewDetachedFromWindow(v);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? };
? ? ? ? }
? ? ? ? final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
newListener,?R.id.onAttachStateChangeListener);
? ? ? ? if (oldListener != null) {
? ? ? ? ? ? view.removeOnAttachStateChangeListener(oldListener);
? ? ? ? }
? ? ? ? if (newListener != null) {
? ? ? ? ? ? view.addOnAttachStateChangeListener(newListener);
? ? ? ? }
? ? }
}
上面的例子是比正常的稍微復雜肛度,因為 View 使用添加和刪除的監(jiān)聽者而不是為 View.OnAttachStateChangeListener 設置方法傻唾,android.databinding.adapters.listenerutil 類有助于保持跟蹤,他們可能會在綁定適配器刪除以前的 listener
https://blog.csdn.net/willba/article/details/71552525時間監(jiān)聽
事件綁定方式
監(jiān)聽方法調用控件常用的監(jiān)聽都可以用事件綁定來搞定承耿,比如以下這幾種
android:onClick
android:onLongClick
android:onTextChanged
其他事件同理
Lambda表達式
在xml中我們還可以應用lambda表達式來書寫
android:onClick="@{(view) -> activity.onTextClick(view)}"
統(tǒng)計一下事件監(jiān)聽表達的用法
由此可見事件監(jiān)聽綁定有很多種用法冠骄,我們在項目中最好統(tǒng)一規(guī)范應用其中一種,避免個性化
android:onClick="onTextClick"
android:onClick="@{(view) -> activity.onTextClick(view)}"
android:onClick="@{activity::onTextClick}"
android:onClick="@{activity.onTextClick}"
問題匯總:
直接 Binding
當一個 Variable 或 Observable 變化時加袋,binding 會在下一幀前被計劃要求改變凛辣,但是有很多次在 Binding 時必需立即執(zhí)行,要強制執(zhí)行职烧,使用 executePendingBindings() 方法
后臺線程
只要它不是一個集合扁誓,你可以在后臺中改變數(shù)據(jù)模型,在判斷是否要避免任何并發(fā)問題時蚀之,Data Binding 會對每個 Variable/field本地化
線程問題
無論在子線程還是主線程中更新ViewModle數(shù)據(jù)蝗敢,Binding框架在更新Ui時都是將交給主線更關心uI,通常來說不會需要我們再做同步的事情足删,但是如果是RecycleView寿谴,則需要再UI線程中更新數(shù)據(jù),因為列表在滑動過程中不斷binging新Item失受,UI線程會不斷讀取viewModle中的數(shù)據(jù)(get方法)讶泰,如果此時再子線程更新了uI線程正在讀取的數(shù)據(jù)容易出現(xiàn)線程問題。