@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//1.獲取ViewDataBinding對象
DataBingMain dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
//2.獲取數據
UserEntity user = new UserEntity();
user.setName("lxf");
user.setSex("man");
user.setAge(25);
user.setType(1);
//3.綁定數據
dataBinding.setUser(user);
//dataBinding.setVariable(BR.user,user);
}</code></pre>
model代碼
package lxf.androiddemos.model;
public class UserEntity {
private String name;
private String sex;
private int age;
private int type;
public String initType(int type){
String result;
switch (type){
case 1:
result = "程序猿";
break;
case 2:
result = "程序猿的天敵";
break;
default:
result = "無業游民";
break;
}
return result;
}
//setter getter方法略
}
activity和model代碼很簡單,就不需要解釋了。
Fragment
看到這里應該有個疑問:fragment中沒有setContentView方法,該怎么辦?
所幸DataBinding庫還提供了另外一個初始化布局的方法:DataBindingUtil.inflate()。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewDataBinding binding = DataBindingUtil.inflate(inflater,R.layout.fragment_blank,container,false);
return binding.getRoot();
}
xml布局的寫法同activity。
列表綁定
在此已RecyclerView為例。
單布局
先看item布局:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="item"
type="lxf.androiddemos.model.MainRecyclerItem"/>
</data>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
app:cardElevation="5dp"
android:onClick="@{item.onItemClick}"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="18sp"
android:textColor="@color/text_green_bg"
android:text="@{item.content}"/>
</android.support.v7.widget.CardView>
</layout>
這里值得我們關注的有兩個地方,一個就是TextView上的數據綁定,一個是父布局上的onClick屬性,可以通過這種方式來設置item點擊事件,說白了其實就是調用MainRecyclerItem中的一個方法,我們可以通過getter方法很方便的知道當前item的具體數據,具體實現請往下看。
model實體類
public class MainRecyclerItem {
public static final String[] items = new String[]{"ViewDragHelper", "自定義Behavior", "二維碼", "DataBinding"};
private String content;
public void onItemClick(View view) {
Intent intent = null;
switch (getContent()) {
case "ViewDragHelper":
intent = new Intent(view.getContext(), ViewDragHelperActivity.class);
break;
case "自定義Behavior":
intent = new Intent(view.getContext(), BehaviorActivity.class);
break;
case "二維碼":
intent = new Intent(view.getContext(), ZxingActivity.class);
break;
case "DataBinding":
intent = new Intent(view.getContext(), DatabindingActivity.class);
break;
}
if (intent != null)
view.getContext().startActivity(intent);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
adapter
class RecyclerBindingViewHolder extends RecyclerView.ViewHolder {
ViewDataBinding binding;
private RecyclerBindingViewHolder(ViewDataBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
static RecyclerBindingViewHolder createViewHolder(ViewGroup parent, int layoutId) {
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),layoutId,parent,false);
return new RecyclerBindingViewHolder(binding);
}
public abstract class BaseRecyclerBindingAdapter extends RecyclerView.Adapter<RecyclerBindingViewHolder> implements ChangeDataLinstener{
protected List<Object> mData;
public BaseRecyclerBindingAdapter(List<Object> list) {
mData = (list != null) ? list : new ArrayList<>();
}
@Override
public RecyclerBindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return RecyclerBindingViewHolder.createViewHolder(parent,getItemLayoutId(viewType));
}
@Override
public void onBindViewHolder(RecyclerBindingViewHolder holder, int position) {
//綁定數據
holder.binding.setVariable(getItemVariableId(),mData.get(position));
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return mData.size();
}
public void setmData(List<Object> mData) {
this.mData = mData;
notifyDataSetChanged();
}
//item布局id
public abstract int getItemLayoutId(int viewType);
//對應item布局里面data標簽中的name,會自動生成一個BR.xxx屬性,類似于R文件
public abstract int getItemVariableId();
}
我們這里把adapter寫成了一個抽象類,如果沒有什么很奇葩的要求,可以算一個通用adapter了,可以看到它沒有任何的findviewbyid和set數據,一切都在布局中封裝好了,實現非常的簡潔。
多布局
如果我們想加一個頭部文件,可以這樣:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="lxf.androiddemos.model.MainRecyclerHeader"/>
</data>
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="center"
android:text="@{item.header}"
android:background="@color/text_orange_bg">
</TextView>
</layout>
注意和之前的item布局有個相通之處,就是data標簽的name屬性值是一樣的,然后通過getItemViewType實現我們的不同布局即可。
datas = new ArrayList<>();
MainRecyclerHeader header = new MainRecyclerHeader();
header.setHeader("我是頭部文件");
datas.add(header);
for (int i = 0; i < MainRecyclerItem.items.length; i++) {
MainRecyclerItem item = new MainRecyclerItem();
item.setContent(MainRecyclerItem.items[i]);
datas.add(item);
}
BaseRecyclerBindingAdapter bindingAdapter = new BaseRecyclerBindingAdapter(datas) {
@Override
public int getItemLayoutId(int viewType) {
return viewType;
}
@Override
public int getItemVariableId() {
return BR.item;//對應item布局里面data標簽中的name
}
@Override
public int getItemViewType(int position) {
if (position == 0)
return R.layout.header_recycler_main;
else
return R.layout.item_recycler_main;
}
};
事件綁定
事件綁定說白了,其實就是一種特殊的變量綁定,或者說是一個方法的調用。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!--如果本頁不需要綁定數據,data標簽可以省略-->
<data class="DataBingMain">
<variable
name="user"
type="lxf.androiddemos.model.UserEntity"/>
<variable
name="util"
type="lxf.androiddemos.test.TestUtil"/>
</data>
<LinearLayout
...
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{util.onBtnClick}"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{util.onTextChanged}"
/>
</LinearLayout>
</layout>
public class TestUtil {
public void onBtnClick(View view){
Toast.makeText(view.getContext(),"onBtnClick",Toast.LENGTH_SHORT).show();
}
public void onTextChanged(CharSequence s, int start, int before, int count){
System.out.println(s);
}
}
需要注意的是通過onclick這種方式綁定的事件,實現方法中一定要傳入view參數(類似于傳統的onClick方法),否則編譯會報錯。
同樣的官方文檔提到,你也可以用這種方式來綁定一些比較偏門的監聽,比如上面的onTextChanged,方法參數必須與傳統的onTextChanged參數一模一樣,否則編譯報錯,這種方式可以使你只監聽onTextChanged一個方法,而非TextWatcher的三個方法,另外EditText本身是沒有android:onTextChanged這個屬性的,具體實現原理需要先理解一下什么是databinding的自定義屬性,會在后文提到。
進階使用
數據更新
在很多情況下,我們需要動態去設置相關數據,DataBinding為我們提供了兩種方式來實現它。
方法一
- 實體類繼承BaseObservable,或者自己實現Observable
- 在需要刷新的屬性的get方法上添加@Bindable注解,此時會自動生成BR類。(這里有個坑,很多時候BR文件不會自動生成,此時需要重啟AS...請讓我先默默地日一波dog)
- 在相應的set方法里調用notifyPropertyChanged(BR.xxx)進行刷新。
package lxf.androiddemos.model;
import android.databinding.BaseObservable;
import android.databinding.Bindable;
import android.view.View;
import lxf.androiddemos.BR;
public class UserEntity extends BaseObservable{
private String name;
private String sex;
private int age;
private int type;
...
public void addAge(View view) {
setAge(getAge() + 1);
}
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
// notifyChange();//刷新所有可刷新數據
notifyPropertyChanged(BR.age);
}
}
方法二
- 實體類繼承BaseObservable,或者自己實現Observable
- 使用ObservableField<>,泛型可以填入自己需要的類型,注意必須要初始化。對于基本數據類型也可以直接使用ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble和ObservableParcelable。
- 通過set和get方法為ObservableField設值和取值
package lxf.androiddemos.model;
import android.databinding.BaseObservable;
import android.databinding.ObservableField;
import android.view.View;
public class UserEntity extends BaseObservable{
public ObservableField<String> address = new ObservableField<>();
public void changeAddress(View view){
address.set("change:" + address.get());
}
}
自定義屬性綁定適配器和回調
自動尋找setter
DataBinding在遇到屬性綁定時,會自動去尋找該屬性的set方法,找到就會調用,找不到就報錯。
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
app:imageResource="@{R.mipmap.ic_launcher}"/>
比如上面這段代碼,我們知道ImageView中是沒有imageResource這個屬性的,但是有setImageResource(int resId)方法,因此這段代碼是可以正常運行的。利用這種特性,可以為一些自定義控件增加setter方法,使其支持DataBinding。
@BindingMethods
當xml屬性名稱和源碼中set方法名稱不一致時,可以通過這種方式來進行綁定。先看一個官方的實現:
@BindingMethods({
...
@BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),
...
})
這段代碼的意思就是將TextView的android:inputType屬性綁定到setRawInputType方法,其實也可以通俗的認為是為原本的setter方法起了一個別名。
@BindingAdapter
很多時候,源碼中并沒有提供set方法,比如ImageView,我們希望通過設置url來達到加載圖片的目的,我們可以通過@BindAdapter來實現。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--如果本頁不需要綁定數據,data標簽可以省略-->
<data class="DataBingMain">
<import type="lxf.androiddemos.R"/>
<variable
name="user"
type="lxf.androiddemos.model.UserEntity"/>
</data>
<LinearLayout
...
>
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:url="@{user.img}"
app:placeHolder="@{R.mipmap.ic_launcher}"/>
</LinearLayout>
</layout>
public class UserEntity extends BaseObservable{
private String img;
@BindingAdapter(value = {"url","placeHolder"},requireAll = false)
public static void setImage(ImageView imageView ,String url,int placeHolder){
Glide.with(imageView.getContext()).load(url).placeholder(placeHolder).into(imageView);
}
}
這里有幾點需要注意:
- xml文件中一定不要忘記各種類的import(除java.lang包外均需導入),否則你一定會碰到databinding程序包不存在這個錯誤。
- 設置@BindAdapter注解的方法需要是static的,否則編譯也會報錯。
- 你可以把這個方法設置在一個專門的工具類中,不是說必須要在這個model實體類里。
- @BindAdapter包含value和requireAll兩個屬性,value是一個String[],包含你自定義的屬性。requireAll意思是是否需要設置你在value中聲明的全部屬性,默認為true。如果設定為false,那么沒賦值的自定義屬性會傳默認值。
到這里,我們來回頭看一下之前在 事件綁定 中留下的那個坑——onTextChanged,其實這是官方提前為我們封裝好的:
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (before != null) {
before.beforeTextChanged(s, start, count, after);
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (on != null) {
on.onTextChanged(s, start, before, count);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
@Override
public void afterTextChanged(Editable s) {
if (after != null) {
after.afterTextChanged(s);
}
}
};
}
final TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
if (oldValue != null) {
view.removeTextChangedListener(oldValue);
}
if (newValue != null) {
view.addTextChangedListener(newValue);
}
}
可以看到,當on!=null時,會調用傳統的onTextChanged方法。
@BindingConversion
方法注釋,當自定義的屬性和setter方法中需要的參數類型不符時進行轉換。
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@{`#de325e`}"
/>
上面這種寫法,直接編譯是會報錯的,因為setBackground接收的是一個Drawable對象,而我們傳入的是個string,所以我們此處可以用@BindingConversion來轉換一下(PS:我知道傳統寫法是可以直接傳字符串顏色值的,我只是舉個簡單例子)。
@BindingConversion
public static Drawable colorToDrawable(String color){
return new ColorDrawable(Color.parseColor(color));
}
DataBinding在碰到這種參數類型不對的問題時,會自動去檢索看看有沒有相關的@BindingConversion方法,如果有的話則會調用,需要注意,這個方法也需要是static的。
接口回調
model的回調
當屬性值變化時的回調。
user.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == BR.age){
Toast.makeText(getApplicationContext(),"age刷新了",Toast.LENGTH_SHORT).show();
}
}
});
ViewDatabinding的回調
當ViewDataBinding執行executePendingBindings()尺寸必須再次評估時的回調。可以設置一些view的展示動畫等。
dataBinding.addOnRebindCallback(new OnRebindCallback() {
@Override
public boolean onPreBind(ViewDataBinding binding) {
return super.onPreBind(binding);
}
@Override
public void onCanceled(ViewDataBinding binding) {
super.onCanceled(binding);
}
@Override
public void onBound(ViewDataBinding binding) {
super.onBound(binding);
}
});
雙向綁定
基本數據
雙向綁定意思不僅數據綁定UI,同時UI更新時可以刷新數據,語法為@={},舉個例子:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{util.onTextChanged}"
android:text="@={user.address}"
/>
這樣在程序運行后,editText會自動顯示user.address的初始值,改變editText,則user.address也會同步改變,可以想象,如果我們將user.address綁定另一個TextView,則TextView的內容會跟隨editText的變化而變化。
隱式屬性監聽
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:visibility="@{checkbox.checked?View.VISIBLE:View.GONE}"
app:placeHolder="@{R.mipmap.ic_launcher}"
app:url="@{user.img}" />
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
可以看ImageView的visibility屬性,通過CheckBox的checked屬性來控制自身的顯示和隱藏,這是官方給出的一種支持,同時官方還支持下面這些屬性:
- AbsListView android:selectedItemPosition
- CalendarView android:date
- CompoundButton android:checked
- DatePicker android:year, android:month, android:day (yes, these are synthetic, but we had a listener, so we thought you’d want to use them)
- NumberPicker android:value
- RadioGroup android:checkedButton
- RatingBar android:rating
- SeekBar android:progress
- TabHost android:currentTab (you probably don’t care, but we had the listener)
- TextView android:text
- TimePicker android:hour, android:minute (again, synthetic, but we had the listener)
自定義雙向綁定
雙向綁定其實就是正向綁定+反向綁定,前面講的全部是正向綁定,截下來我們來看看怎么定義反向綁定。
綁定方法(@InverseBindingMethods)
首先先來了解幾個名詞:
event調用時機需要通過@BindingAdapter進行設置。
- InverseBindingListener:反向綁定監聽器,當使用雙向綁定時,會在你的layout自動生成的binding類中自動生成一個InverseBindingListener的實現(拗口嗎?好像有一點點。。不理解的可以去看看源碼)。
看完這幾個名詞是不是已經凌亂了?(話說我當時也差點哭了。。),我們來看個官方例子消化一下:
@InverseBindingMethods({
@InverseBindingMethod(type = CompoundButton.class, attribute = "android:checked"),
})//1.這里需要雙向綁定的是checked屬性,event和method都省略了。
public class CompoundButtonBindingAdapter {
...
//2.設置什么時候調用event
@BindingAdapter(value = {"android:onCheckedChanged", "android:checkedAttrChanged"},
requireAll = false)
public static void setListeners(CompoundButton view, final OnCheckedChangeListener listener,
final InverseBindingListener attrChange) {
if (attrChange == null) {
view.setOnCheckedChangeListener(listener);
} else {
view.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (listener != null) {
listener.onCheckedChanged(buttonView, isChecked);
}
attrChange.onChange();
}
});
}
}
}
//3.我們在layout中使用雙向綁定
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={user.checked}"/>
//4.layout的binding類中自動生成的InverseBindingListener實現。
// Inverse Binding Event Handlers
private android.databinding.InverseBindingListener checkboxandroidCheck = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {//這段邏輯其實就是用來更新user實體類中的checked字段的
// Inverse of user.checked.get()
// is user.checked.set((java.lang.Boolean) callbackArg_0)
boolean callbackArg_0 = checkbox.isChecked();//其實就是method
// localize variables for thread safety
// user.checked != null
boolean checkedUserObjectnul = false;
// user.checked
android.databinding.ObservableField<java.lang.Boolean> checkedUser = null;
// user
lxf.androiddemos.model.UserEntity user = mUser;
// user.checked.get()
java.lang.Boolean CheckedUser1 = null;
// user != null
boolean userObjectnull = false;
userObjectnull = (user) != (null);
if (userObjectnull) {
checkedUser = user.checked;
checkedUserObjectnul = (checkedUser) != (null);
if (checkedUserObjectnul) {
checkedUser.set((java.lang.Boolean) (callbackArg_0));
}
}
}
};
整個反向綁定的流程下來其實就是:
- 定義需要反向綁定的屬性(checked),并配置event(checkedAttrChanged)和method(isChecked)。
- 系統會自動根據event找到對應的方法(setLinstener),配置好調用時機。
- 開發者在layout中使用雙向綁定。
- 自動在binding類中生成一個InverseBindingListener的實現。
綁定適配器(@InverseBindingAdapter)
下面再來看個新名詞...( ╯□╰ ):
- @InverseBindingAdapter:反向綁定適配器,用來注解 方法 。只包含attribute和event兩個屬性,含義同上:
- attribute:支持雙向綁定的屬性(string格式)。
- event:可以省略,用來通知DataBinding系統attribute已經改變,默認為attribute + "AttrChanged"。需要通過@BindingAdapter進行設置調用時機。
@InverseBindingAdapter注解的方法本身就相當于獲取數據的getter方法(類似于@BindingAdapter注解的方法本身就相當于setter方法)。
官方案例(雙向綁定android:text):
//1.這一步相當于做了兩個操作:確定綁定的屬性和event;指定getter方法
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
}
//2.根據event找到對應方法,配置event的調用時機。
@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
"android:afterTextChanged", "android:textAttrChanged"}, requireAll = false)
public static void setTextWatcher(TextView view, final BeforeTextChanged before,
final OnTextChanged on, final AfterTextChanged after,
final InverseBindingListener textAttrChanged) {
final TextWatcher newValue;
if (before == null && after == null && on == null && textAttrChanged == null) {
newValue = null;
} else {
newValue = new TextWatcher() {
...
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (on != null) {
on.onTextChanged(s, start, before, count);
}
if (textAttrChanged != null) {
textAttrChanged.onChange();
}
}
....
};
}
...
}
//3.使用雙向綁定
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onTextChanged="@{util.onTextChanged}"
android:text="@={user.address}" />
//4.binding類中自動生成InverseBindingListener的實現。
private android.databinding.InverseBindingListener mboundView10androidT = new android.databinding.InverseBindingListener() {
@Override
public void onChange() {
// Inverse of user.address.get()
// is user.address.set((java.lang.String) callbackArg_0)
java.lang.String callbackArg_0 = android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView10);//getter方法
// localize variables for thread safety
// user.address != null
boolean addressUserObjectnul = false;
// user
lxf.androiddemos.model.UserEntity user = mUser;
// user.address
android.databinding.ObservableField<java.lang.String> addressUser = null;
// user.address.get()
java.lang.String AddressUser1 = null;
// user != null
boolean userObjectnull = false;
userObjectnull = (user) != (null);
if (userObjectnull) {
addressUser = user.address;
addressUserObjectnul = (addressUser) != (null);
if (addressUserObjectnul) {
addressUser.set((java.lang.String) (callbackArg_0));
}
}
}
};
一大堆的代碼看下來,其實綁定方法和綁定適配器兩種方法的最終效果是一樣的,實現過程也是大同小異,這里就不贅述了,和上面的綁定方法基本一致。
比葫蘆畫瓢
我們來自定義實現這樣一個效果,點擊改變自定義view的顏色,同時將色值在另一個TextView中展示出來(雖然沒什么卵用,僅僅當個案例吧),效果圖如下(請自覺忽略其他的東西。。):

效果圖
實現過程:
//1.自定義ColorPicker,并為color屬性添加getter和setter方法
public class ColorPicker extends View {
...
private String mColor;
public String getColor() {
return mColor;
}
public void setColor(String mColor) {
this.mColor = mColor;
paint.setColor(Color.parseColor(mColor));
invalidate();
}
...
}
//2.自定義反向綁定
@InverseBindingMethods({
@InverseBindingMethod(type = ColorPicker.class,attribute = "color")
})
public class ColorPickerAdapter {
@BindingAdapter(value = {"colorAttrChanged"},requireAll = false)
public static void setListener(ColorPicker picker, final InverseBindingListener attrChange){
if (attrChange!=null){
picker.setOnColorChangeListener(new ColorPicker.OnColorChangeListener() {
@Override
public void onColorChange(ColorPicker picker, String color) {
//...
attrChange.onChange();
}
});
}
}
}
//3.在layout中使用雙向綁定
<lxf.androiddemos.test.ColorPicker
android:layout_width="100dp"
android:layout_height="100dp"
app:color="@={user.color}" />
上面給出了關鍵代碼,剛接觸DataBinding的萌新如果理解不了可以去文末下載Demo看看,只是一個很簡單的案例,應該沒什么問題。
接下來我們用@InverseBindingAdapter來實現同樣的效果:
public class ColorPickerAdapter {
@InverseBindingAdapter(attribute = "color")
public static String getColor(ColorPicker picker){
return picker.getColor();
}
@BindingAdapter(value = {"colorAttrChanged"},requireAll = false)
public static void setListener(ColorPicker picker, final InverseBindingListener attrChange){
if (attrChange!=null){
picker.setOnColorChangeListener(new ColorPicker.OnColorChangeListener() {
@Override
public void onColorChange(ColorPicker picker, String color) {
//...
attrChange.onChange();
}
});
}
}
}
另外關于一些情況下雙向綁定存在的死循環問題,只要在setter方法中判斷一下新老值不同即可。
依賴注入
DataBindingComponent,一般用于一個@BindingAdapter方法需要有多種實現時(比如說測試。。),我們來看一下前面那個修改年齡age的例子:
//原來的方式
@BindingAdapter(value = {"url","placeHolder"},requireAll = false)
public static void setImage(ImageView imageView , String url, int placeHolder){
ImgLoadUtil.load(imageView,url,placeHolder);
}
//運用DataBindingComponent
//1.如果需要多種實現,可以先建一個抽象的adapter,注意方法為非靜態的
public abstract class AppAdapter {
@BindingAdapter(value = {"url","placeHolder"},requireAll = false)
public abstract void setImage(ImageView imageView , String url, int placeHolder);
}
//2.添加抽象adapter的實現,這里我們只寫了一個
public class ImgAdapter extends AppAdapter {
@Override
public void setImage(ImageView imageView, String url, int placeHolder) {
ImgLoadUtil.load(imageView,url,placeHolder);
}
}
public class Img2Adapter extends AppAdapter {
@Override
public void setImage(ImageView imageView, String url, int placeHolder) {
...
}
}
//3.添加DataBindingComponent的實現(非靜態的@BindingAdapter注解方法會自動在DataBindingComponent中生成相應的getter方法)。
public class MyComponent implements android.databinding.DataBindingComponent {
@Override
public AppAdapter getAppAdapter() {
return new ImgAdapter();
}
}
public class My2Component implements android.databinding.DataBindingComponent {
@Override
public AppAdapter getAppAdapter() {
return new Img2Adapter();
}
}
//4.Activity中調用
//DataBingMain dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
//DataBindingUtil.setDefaultComponent(new MyComponent());
DataBingMain dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_databinding,new MyComponent());
最終效果是一模一樣的。
遇到的坑
-
Error:(8, 36) 錯誤: 程序包lxf.androiddemos.databinding不存在
遇到這種情況一般都是xml中的問題,比如data標簽中引入的包名不對,或者是布局里面使用了什么錯誤的屬性,等等。。。數據量大的時候,這種錯誤一般比較難找,簡直就是日了dog。
-
需要更新數據時,為getter方法設置@Bindable,很多時候BR文件不會生成,需要重啟AS,默默地再日一波dog。
- 最好不要使用clean project,否則R文件和BR文件會被清掉,R文件會自動重新生成,至于BR文件...那只dog,麻煩你再過來一下。
來自:http://www.jianshu.com/p/05b9838a1949