開篇廢話
公司走了一個(gè)人,那個(gè)人寫的程序使用到了DataBinding腌乡,既然這樣,我就必須學(xué)習(xí)DataBinding,盡快接手這個(gè)項(xiàng)目繁调。
DataBinding解決了Android UI編程中的一個(gè)痛點(diǎn)晒来,官方原生支持MVVM模型可以讓我們在不改變既有代碼框架的前提下竭宰,非常容易地使用這些新特性豫喧。
MVVM的介紹
MVVM是Model-View-ViewModel的簡寫,這個(gè)模式提供對View和View Model的雙向數(shù)據(jù)綁定谦絮,使得View Model的狀態(tài)改變可以自動傳遞給View题诵。
- Model:數(shù)據(jù)層洁仗,負(fù)責(zé)處理數(shù)據(jù)的加載或者存儲。
- View:視圖層性锭,負(fù)責(zé)界面數(shù)據(jù)的展示赠潦,與用戶進(jìn)行交互。
- ViewModel:負(fù)責(zé)完成View于Model間的交互,負(fù)責(zé)業(yè)務(wù)邏輯草冈。
MVVM的模型關(guān)系圖:
準(zhǔn)備工作
DataBinding是一個(gè)support library她奥,所以它可以支持所有的android sdk,最低可以到android2.1(API7)怎棱。
如果是Android studio的版本在2.1以上哩俭,Android studio內(nèi)置就支持了 DataBiding。如果是2.1之前的版本最好是升級一下拳恋,然后只需要在對應(yīng)的Module的build.gradle中添加這么一句話即可凡资。
dataBinding {
enabled=true
}
或
dataBinding.enabled=true
如果Android studio的版本不在2.1以上,那么就需要使用以下方面了谬运,如果高于2.1隙赁,直接跳到基礎(chǔ)操作看。
使用DataBinding需要Android Gradle插件的支持梆暖,版本至少在1.5以上伞访,需要的Android studio的版本在1.3以上。用如下方法導(dǎo)入轰驳。
AS 2.1以下修改build.gradle
再次提示一下厚掷,如果Android studio的版本在2.1以下,那么就需要使用以下方面了级解,如果高于2.1冒黑,直接跳到基礎(chǔ)操作看。
修改 Project 的build.gradle蠕趁,為 build script 添加一條依賴薛闪,Gradle 版本為 1.2.3辛馆。
classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.android.databinding:dataBinder:1.0-rc0'
為用到 DataBinding 的模塊添加插件俺陋,修改對應(yīng)的build.gradle。
apply plugin: 'com.android.databinding'
注意
如果 Module 用到的 buildToolsVersion 高于 22.0.1昙篙,比如 23 rc1腊状,那 com.android.databinding:dataBinder 的版本要改為 1.3.0-beta1,否則會出現(xiàn)如下錯(cuò)誤:
基礎(chǔ)操作
工程創(chuàng)建完成后苔可,我們通過一個(gè)最簡單的例子來說明 DataBinding 的基本用法缴挖。
布局文件
使用 DataBinding 之后,xml的布局文件就不再單純地展示 UI 元素焚辅,還需要定義 UI 元素用到的變量映屋。所以苟鸯,它的根節(jié)點(diǎn)不再是一個(gè)ViewGroup,而是變成了layout棚点,并且新增了一個(gè)節(jié)點(diǎn)data早处。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<!--原先的根節(jié)點(diǎn)(Root Element)-->
<LinearLayout>
....
</LinearLayout>
</layout>
要實(shí)現(xiàn) MVVM 的ViewModel 就需要把數(shù)據(jù)與UI進(jìn)行綁定,data節(jié)點(diǎn)就為此提供了一個(gè)橋梁瘫析,我們先在data 中聲明一個(gè)variable砌梆,這個(gè)變量會為UI 元素提供數(shù)據(jù)(例如 TextView 的 android:text),然后在Java代碼中把”后臺”數(shù)據(jù)與這個(gè)variable進(jìn)行綁定贬循。
如果要用一個(gè)表格來展示用戶的基本信息咸包,用 DataBinding 應(yīng)該怎么實(shí)現(xiàn)呢?
數(shù)據(jù)對象
添加一個(gè) POJO類 - User杖虾,非常簡單烂瘫,四個(gè)屬性以及他們的getter和setter。
public class User {
private final String firstName;
private final String lastName;
private String displayName;
private int age;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName);
this.age = age;
}
public int getAge() {
return age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getDisplayName() {
return firstName + " " + lastName;
}
public boolean isAdult() {
return age >= 18;
}
}
稍后亏掀,我們會新建一個(gè)User類型的變量忱反,然后把它跟布局文件中聲明的變量進(jìn)行綁定。
定義Variable
再回到布局文件滤愕,在data節(jié)點(diǎn)中聲明一個(gè)變量user温算。
<data>
<variable name="user" type="com.cc.databinding.User" />
</data>
其中type屬性就是我們在Java文件中定義的User類。
當(dāng)然间影,data節(jié)點(diǎn)也支持import注竿,所以上面的代碼可以換一種形式來寫。
<data>
<import type="com.cc.databinding.User" />
<variable name="user" type="User" />
</data>
然后我們剛才在 build.gradle 中添加的那個(gè)插件 - com.android.databinding會根據(jù)xml文件的名稱Generate一個(gè)繼承自ViewDataBinding的類魂贬。
例如巩割,這里xml的文件名叫activity_basic.xml,那么生成的類就是ActivityBasicBinding付燥。
注意
java.lang.*包中的類會被自動導(dǎo)入宣谈,可以直接使用,例如要定義一個(gè)String類型的變量:
<variable name="firstName" type="String" />
綁定Variable
Activity
修改BasicActivity的onCreate方法键科,用DatabindingUtil.setContentView()來替換掉setContentView()闻丑,然后創(chuàng)建一個(gè)user對象,通過binding.setUser(user)與variable進(jìn)行綁定勋颖。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityBasicBinding binding = DataBindingUtil.setContentView(
this, R.layout.activity_basic);
User user = new User("guo", "cc");
binding.setUser(user);
}
Fragment
所幸DataBinding庫還提供了另外一個(gè)初始化布局的方法: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();
}
注意
ActivityBasicBinding類是自動生成的,所有的set方法也是根據(jù)variable名稱生成的饭玲。例如侥祭,我們定義了兩個(gè)變量。
<data>
<variable name="firstName" type="String" />
<variable name="lastName" type="String" />
</data>
那么就會生成對應(yīng)的兩個(gè) set 方法。
setFirstName(String firstName);
setLastName(String lastName);
使用 Variable
數(shù)據(jù)與 Variable 綁定之后矮冬,xml 的 UI 元素就可以直接使用了谈宛。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}" />
至此,一個(gè)簡單的數(shù)據(jù)綁定就完成了胎署。
高級用法
綁定非Activity的onClick寫法
android:onClick="@{(view)->user.show(view)}"
使用類方法
首先為類添加一個(gè)靜態(tài)方法入挣。
public class MyStringUtils {
public static String capitalize(final String word) {
if (word.length() > 1) {
return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
}
return word;
}
}
然后在xml的data節(jié)點(diǎn)中導(dǎo)入:
<import type="com.cc.databinding.MyStringUtils" />
使用方法與 Java 語法一樣:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{MyStringUtils.capitalize(user.firstName)}" />
類型別名
如果我們在data節(jié)點(diǎn)了導(dǎo)入了兩個(gè)同名的類怎么辦?
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" />
<variable name="user" type="User" />
這樣一來出現(xiàn)了兩個(gè)User類硝拧,那user變量要用哪一個(gè)呢径筏?不用擔(dān)心,import還有一個(gè)alias屬性障陶。
<import type="com.example.home.data.User" />
<import type="com.examle.detail.data.User" alias="DetailUser" />
<variable name="user" type="DetailUser" />
Null Coalescing 運(yùn)算符
android:text="@{user.displayName ?? user.lastName}"
就等價(jià)于
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
屬性值
通過@{}可以直接把Java中定義的屬性值賦值給xml屬性滋恬。
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
別忘了導(dǎo)包,否則View.VISIBLE和View.GONE不可以使用抱究。
<import type="android.view.View"/>
使用資源數(shù)據(jù)
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
完整版的布局文件如下:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 記得在前面加個(gè)“.” -->
<data class=".ResourceBinding">
<variable name="large" type="boolean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
android:background="@android:color/black"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</LinearLayout>
</layout>
largePadding和smallPadding都是定義在dimens.xml文件中的資源數(shù)據(jù)恢氯。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="largePadding">20dp</dimen>
<dimen name="smallPadding">5dp</dimen>
</resources>
在Java代碼中與綁定large變量,并賦值為ture鼓寺。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ResourceBinding不需要導(dǎo)包勋拟,導(dǎo)包就錯(cuò)了
ResourceBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_resource);
binding.setLarge(true);
}
這樣做是沒有什么問題的,但是有老版本的可能會出問題妈候。
如果在Run工程的時(shí)候敢靡,出現(xiàn)錯(cuò)誤,報(bào)錯(cuò)信息如下:
cannot find the setter for attribute 'android:padding' on android.widget.TextView with parameter type float.
看來像是DataBinder把@dimen/largePadding解析成了float類型苦银,可以試一下類型轉(zhuǎn)換:
android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"
雙向綁定
在舊版本只支持單向綁定啸胧,建議升級為最新版。
在正向綁定中幔虏,我們在Layout里面的綁定表達(dá)式是這樣的:
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<TextView android:text="@{user.name}" .../>
</RelativeLayout>
</layout>
當(dāng)user.name的數(shù)據(jù)改動時(shí)纺念,我們的TextView都會同步改變文字。
現(xiàn)在假設(shè)一種情況想括,當(dāng)你更換成EditText時(shí)陷谱,如果你的用戶名User.name已經(jīng)綁定到EditText中,當(dāng)用戶輸入文字的時(shí)候瑟蜈,你原來的user.name數(shù)據(jù)并沒有同步改動烟逊,因此我們需要修改成:
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<EditText android:text="@={user.name}" .../>
</RelativeLayout>
</layout>
看出微小的差別了嗎?對踪栋,就是"@{}"改成了"@={}"焙格,是不是很簡單图毕?
開啟雙向綁定夷都,需要在項(xiàng)目的build.gradle中設(shè)置:
classpath 'com.android.tools.build:gradle:2.1.0-alpha3'
我們剛才的例子里面只顯示了系統(tǒng)自帶的應(yīng)用,那么如果是自定義控件,或者是我們更細(xì)顆粒度的Observable呢囤官?等下就揭曉如何自定義自己的雙向綁定冬阳,我們來看看目前Android支持的控件:
- AbsListView android:selectedItemPosition
- CalendarView android:date
- CompoundButton android:checked
- DatePicker android:year, android:month, android:day
- NumberPicker android:value
- RadioGroup android:checkedButton
- RatingBar android:rating
- SeekBar android:progress
- TabHost android:currentTab (估計(jì)沒人用)
- TextView android:text
- TimePicker android:hour, android:minute
自定義雙向綁定
設(shè)想一下我們使用了下拉刷新SwipeRefreshLayout控件,這個(gè)時(shí)候我們希望在加載數(shù)據(jù)的時(shí)候能控制refreshing的狀態(tài)党饮,所以我們加入了ObservableBoolean的變量swipeRefreshViewRefreshing來正向綁定數(shù)據(jù)肝陪,并且能夠在用戶手動下拉刷新的時(shí)候同步更新swipeRefreshViewRefreshing數(shù)據(jù):
// SwipeRefreshLayout.java
public class SwipeRefreshLayout extends View {
private boolean isRefreshing;
public void setRefreshing() {/* ... */}
public boolean isRefreshing() {/* ... */}
public void setOnRefreshListener(OnRefreshListener listener) {
/* ... */
}
public interface OnRefreshListener {
void onRefresh();
}
}
接下來我們需要告訴框架,我們需要將SwipeRefreshLayout的isRefreshing的值反向綁定到swipeRefreshViewRefreshing:
@InverseBindingMethods({
@InverseBindingMethod(
type = android.support.v4.widget.SwipeRefreshLayout.class,
attribute = "refreshing",
event = "refreshingAttrChanged",
method = "isRefreshing")})
這是一種簡單的定義刑顺,其中event和method都不是必須的氯窍,因?yàn)橄到y(tǒng)會自動生成,寫出來是為了更好地了解如何綁定的蹲堂,可以參考官方文檔InverseBindingMethod狼讨。
當(dāng)然你也可以使用另外一種寫法,并且如果你的值并不是直接對應(yīng)Observable
的值的時(shí)候柒竞,就可以在這里進(jìn)行轉(zhuǎn)換:
@InverseBindingAdapter(attribute = "refreshing", event = "refreshingAttrChanged")
public static boolean isRefreshing(SwipeRefreshLayout view) {
return view.isRefreshing();
}
上面的event同樣也不是必須的政供。以上的定義都是為了讓我們能夠在布局文件中使用"@={}"這個(gè)雙向綁定的特性。接下來你需要告訴框架如何處理refreshingAttrChanged事件朽基,就像處理一般的監(jiān)聽事件一樣:
@BindingAdapter("refreshingAttrChanged")
public static void setOnRefreshListener(final SwipeRefreshLayout view,
final InverseBindingListener refreshingAttrChanged) {
if (refreshingAttrChanged == null) {
view.setOnRefreshListener(null);
} else {
view.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh() {
colorChange.onChange();
}
});
}
}
一般情況下布隔,我們都需要設(shè)置正常的OnRefreshListener,所以我們可以合并寫成:
@BindingAdapter(value = {"onRefreshListener", "refreshingAttrChanged"}, requireAll = false)
public static void setOnRefreshListener(final SwipeRefreshLayout view,
final OnRefreshListener listener,
final InverseBindingListener refreshingAttrChanged) {
OnRefreshListener newValue = new OnRefreshListener() {
@Override
public void onRefresh() {
if (listener != null) {
listener.onRefresh();
}
if (refreshingAttrChanged != null) {
refreshingAttrChanged.onChange();
}
}
};
OnRefreshListener oldValue = ListenerUtil.trackListener(view, newValue, R.id.onRefreshListener);
if (oldValue != null) {
view.setOnRefreshListener(null);
}
view.setOnRefreshListener(newValue);
}
現(xiàn)在我們終于可以使用雙向綁定的技術(shù)啦稼虎。但是要注意衅檀,需要設(shè)置requireAll = false,否則系統(tǒng)將識別不了refreshingAttrChanged屬性霎俩,前文提到的文章例子里并沒有設(shè)置這個(gè)术吝。
在ViewModel中,我們的數(shù)據(jù)是這樣的:
// MyViewModel.java
public final ObservableBoolean swipeRefreshViewRefreshing = new ObservableBoolean(false);
public void load() {
swipeRefreshViewRefreshing.set(true);
// 網(wǎng)絡(luò)請求
....
swipeRefreshViewRefreshing.set(false);
}
public SwipeRefreshLayout.OnRefreshListener onRefreshListener() {
return new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
// Do something you need
}
};
}
在布局文件中是這樣設(shè)置的:
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:onRefreshListener="@{viewModel.onRefreshListener}"
app:refreshing="@={viewModel.swipeRefreshViewRefreshing}">
...
</android.support.v4.widget.SwipeRefreshLayout>
最后我們還有一個(gè)小問題茸苇,就是雙向綁定有可能會出現(xiàn)死循環(huán)排苍,因?yàn)楫?dāng)你通過Listener反向設(shè)置數(shù)據(jù)時(shí),數(shù)據(jù)也會再次發(fā)送事件給View学密。所以我們需要在設(shè)置一下避免死循環(huán):
@BindingAdapter("refreshing")
public static void setRefreshing(SwipeRefreshLayout view, boolean refreshing) {
if (refreshing != view.isRefreshing()) {
view.setRefreshing(refreshing);
}
}
這樣就沒問題啦淘衙。
帶ID的View
DataBinding有效降低了代碼的冗余性,甚至完全沒有必要再去獲取一個(gè)View實(shí)例腻暮,但是情況不是絕對的彤守,萬一我們真的就需要了呢?不用擔(dān)心哭靖,只要給View定義一個(gè) ID具垫,DataBinding就會為我們生成一個(gè)對應(yīng)的final變量。
<TextView
android:id="@+id/firstName"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
上面代碼中定義了一個(gè)ID為firstName*的TextView试幽,那么它對應(yīng)的變量就是筝蚕。
public final TextView firstName;
使用的時(shí)候用。
binding.firstName.setText("cc");
ViewStubs
xml中的ViewStub經(jīng)過 binding 之后會轉(zhuǎn)換成 ViewStubProxy。
簡單用代碼說明一下起宽,xml文件與之前的代碼一樣洲胖,根節(jié)點(diǎn)改為layout,在LinearLayout中添加一個(gè)ViewStub坯沪,添加ID绿映。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
...>
<ViewStub
android:id="@+id/view_stub"
android:layout="@layout/view_stub"
... />
</LinearLayout>
</layout>
在Java代碼中獲取binding實(shí)例,為ViewStubProy注冊ViewStub.OnInflateListener事件腐晾,搞定叉弦!
binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
@Override
public void onInflate(ViewStub stub, View inflated) {
ViewStubBinding binding = DataBindingUtil.bind(inflated);
User user = new User("fee", "lang");
binding.setUser(user);
}
});
Dynamic Variables
以RecyclerView為例,Adapter的DataBinding需要動態(tài)生成藻糖,因此我們可以在onCreateViewHolder的時(shí)候創(chuàng)建這個(gè)DataBinding卸奉,然后在onBindViewHolder中獲取這個(gè)DataBinding。
public static class BindingHolder extends RecyclerView.ViewHolder {
private ViewDataBinding binding;
public BindingHolder(View itemView) {
super(itemView);
}
public ViewDataBinding getBinding() {
return binding;
}
public void setBinding(ViewDataBinding binding) {
this.binding = binding;
}
}
@Override
public BindingHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(viewGroup.getContext()),
R.layout.list_item,
viewGroup,
false);
BindingHolder holder = new BindingHolder(binding.getRoot());
holder.setBinding(binding);
return holder;
}
@Override
public void onBindViewHolder(BindingHolder holder, int position) {
User user = users.get(position);
holder.getBinding().setVariable(BR.user, user);
holder.getBinding().executePendingBindings();
}
注意此處DataBindingUtil的用法:
ViewDataBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(viewGroup.getContext()),
R.layout.list_item,
viewGroup,
false);
Attribute setters
當(dāng)一個(gè)被綁定的數(shù)據(jù)的值發(fā)生改變時(shí)颖御,Binding類會自動尋找該view上的綁定表達(dá)式上的方法去改變view榄棵,通過google數(shù)據(jù)綁定框架我們可以去自定義這些方法。
對于一個(gè)xml的attribute潘拱,DataBinding會去尋找setAttribute方法疹鳄,xml屬性的命名空間是沒有關(guān)系的。比如TextView上的一個(gè)屬性android:text芦岂,會去尋找setText(String)瘪弓。如果表達(dá)式返回的是int則會去尋找setText(int),所以必須確保xml中表達(dá)式返回正確的數(shù)據(jù)類型禽最,必要時(shí)需要數(shù)據(jù)轉(zhuǎn)換腺怯。
我們可以比較容易地為任何屬性創(chuàng)造出setter去使用dataBinding。比如support包下的DrawerLayout沒有任何屬性川无,但是確有很多setter呛占,下面利用這些已有的setter中的一個(gè):
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
自定義setters
一些xml屬性需要自己去定義并實(shí)現(xiàn)邏輯,比如android:paddingLeft懦趋。但是setPadding(left,top,right,bottom)是存在的晾虑,那么我們可以同BindingAdapter注解去自定義個(gè)自己的setter:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
Note:開發(fā)者自定義的BindingAdapter和android自帶的發(fā)生沖突時(shí),data bingding會優(yōu)先采用開發(fā)者自定義的仅叫。
多參數(shù)的BindingAdapter
@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);
}
BindingAdpater方法可以對屬性的舊值和新值進(jìn)行處理
@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: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);
}
}
}
轉(zhuǎn)換器 (Converters)
有時(shí)候我們想這樣寫xml屬性帜篇。
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
但是xml屬性的setter是一個(gè)drawable,我們可以定義一個(gè)標(biāo)記了@BindingConversion的靜態(tài)方法即可诫咱。
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
由于這個(gè)注解至是發(fā)生在setter層面上笙隙,所以并不支持下面的混合寫法。
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
關(guān)于DataBinding的一些個(gè)人看法
DataBinding使用心得
- 使用xml進(jìn)行view布局
- 采用符合Java Bean規(guī)范的數(shù)據(jù)原型
- 規(guī)范的自定義View
- 禁止在BindingAdapter中的setter方法中改變數(shù)據(jù)或者做數(shù)據(jù)處理
- 不建議用BindingConversion處理數(shù)據(jù)轉(zhuǎn)換
- 不建議在xml布局中處理view事件
- 不建議在xml中使用復(fù)雜的表達(dá)式
DataBinding使用的一些思考
DataBinding的不足之處:
- DataBinding在xml提供了豐富的操作符坎缭,但是由于Android studio天生的xml語法檢查的貧弱竟痰,xml布局中的表達(dá)式邏輯錯(cuò)誤签钩,不能準(zhǔn)確定位,導(dǎo)致debug難度增加凯亮,事實(shí)上一些BindingAdapter的錯(cuò)誤在build的時(shí)候也會被提示xml錯(cuò)誤。
- 對自定義view的要求比較高哄尔,需要自定義綁定方法假消,如BindingAdapter等。
- 可能由于java8移除apt岭接,采用了新的API的緣故富拗,所以即使Android Studio2.2已經(jīng)開始支持java8特性,但是需要開啟jack編譯鏈鸣戴,DataBinding與之沖突啃沪,導(dǎo)致在代碼中不能使用lambda表達(dá)式等java8特性。值得欣慰的是窄锅,這一問題將在Android Studio2.4中得到解決创千。
PS:數(shù)據(jù)綁定的應(yīng)用軟件開發(fā)的一種趨勢,使用DataBinding的優(yōu)點(diǎn)顯而易見入偷,但是使用的時(shí)候我們也需要小心追驴。