Android DataBinding的官方雙向綁定示例
在Android Studio 2.1 Preview 3之后,官方開始支持雙向綁定了。
可惜目前Google并沒有在Data Binding指南里面加入這個(gè)教程,并且在整個(gè)互聯(lián)網(wǎng)之中只有這篇文章介紹了如何使用反向綁定。
在閱讀一下文章之前,我假設(shè)你已經(jīng)知道如何正向綁定。
回顧一下Data Binding
在正向綁定中,我們?cè)贚ayout里面的綁定表達(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ù)改動(dòng)時(shí),我們的TextView都會(huì)同步改變文字。
雙向綁定
現(xiàn)在假設(shè)一種情況,當(dāng)你更換成EditText時(shí),如果你的用戶名User.name已經(jīng)綁定到EditText中,當(dāng)用戶輸入文字的時(shí)候,你原來的user.name數(shù)據(jù)并沒有同步改動(dòng),因此我們需要修改成:
<layout ...>
<data>
<variable type="com.example.myapp.User" name="user"/>
</data>
<RelativeLayout ...>
<EditText android:text="@={user.name}" .../>
</RelativeLayout>
</layout>
看出微小的差別了嗎?對(duì),就是"@{}"改成了"@={}",是不是很簡單?
隱式引用屬性
同樣你也可以在別的View上引用屬性:
<layout ...>
<data>
<import type="android.view.View"/>
</data>
<RelativeLayout ...>
<CheckBox android:id="@+id/seeAds" .../>
<ImageView android:visibility="@{seeAds.checked ? View.VISIBLE : View.GONE}" .../>
</RelativeLayout>
</layout>
當(dāng)CheckBox的狀態(tài)發(fā)生改變的時(shí)候,ImageView也會(huì)同時(shí)發(fā)生改變。在復(fù)雜情況下,這個(gè)特性沒什么卵用,因?yàn)檫壿嫴糠治覀兪遣唤ㄗh寫在XML中。
如何開啟雙向綁定
開啟雙向綁定,需要在項(xiàng)目的build.gradle中設(shè)置:
classpath 'com.android.tools.build:gradle:2.1.0-alpha3'
同樣,你需要在你Module的build.gradle中設(shè)置:
android {
...
dataBinding.enabled = true
}
貌似還有點(diǎn)問題
我們剛才的例子里面只顯示了系統(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ù),并且能夠在用戶手動(dòng)下拉刷新的時(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)會(huì)自動(dòng)生成,寫出來是為了更好地了解如何綁定的,可以參考官方文檔InverseBindingMethod。
當(dāng)然你也可以使用另外一種寫法,并且如果你的值并不是直接對(duì)應(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)將識(shí)別不了refreshingAttrChanged屬性,前文提到的文章例子里并沒有設(shè)置這個(gè)。
在ViewModel中,我們的數(shù)據(jù)是這樣的:
// MyViewModel.java
public final ObservableBoolean swipeRefreshViewRefreshing = new ObservableBoolean(false);
public void load() {
swipeRefreshViewRefreshing.set(true);
// 網(wǎng)絡(luò)請(qǐng)求
....
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è)小問題,就是雙向綁定有可能會(huì)出現(xiàn)死循環(huán),因?yàn)楫?dāng)你通過Listener反向設(shè)置數(shù)據(jù)時(shí),數(shù)據(jù)也會(huì)再次發(fā)送事件給View。所以我們需要在設(shè)置一下避免死循環(huán):
@BindingAdapter("refreshing")
public static void setRefreshing(SwipeRefreshLayout view, boolean refreshing) {
if (refreshing != view.isRefreshing()) {
view.setRefreshing(refreshing);
}
}
這樣就沒問題啦。以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android使用ViewBinding的詳細(xì)步驟(Kotlin簡易版)
- Android ViewBinding的使用詳解
- Android Studio 3.6中新的視圖綁定工具ViewBinding 用法詳解
- Android Studio3.6新特性之視圖綁定ViewBinding使用指南
- Android基礎(chǔ)入門之dataBinding的簡單使用教程
- Android開發(fā)Jetpack組件DataBinding用例詳解
- Android DataBinding手把手入門教程
- 在Android中如何使用DataBinding詳解(Kotlin)
- Android淺析viewBinding和DataBinding
相關(guān)文章
Android RecyclerView添加上拉加載更多效果
這篇文章主要為大家詳細(xì)介紹了Android RecyclerView添加上拉加載更多效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
android表格效果之ListView隔行變色實(shí)現(xiàn)代碼
首先繼承SimpleAdapter再使用重載的Adapter來達(dá)到效果,其實(shí)主要是需要重載SimpleAdapter,感興趣的朋友可以研究下,希望本文可以幫助到你2013-02-02
Android折疊式Toolbar使用完全解析(CollapsingToolbarLayout)
這篇文章主要為大家詳細(xì)介紹了Android折疊式Toolbar的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Android音視頻開發(fā)之MediaCodec的使用教程
在Android開發(fā)中提供了實(shí)現(xiàn)音視頻編解碼工具M(jìn)ediaCodec,針對(duì)對(duì)應(yīng)音視頻解碼類型通過該類創(chuàng)建對(duì)應(yīng)解碼器就能實(shí)現(xiàn)對(duì)數(shù)據(jù)進(jìn)行解碼操作。本文通過示例詳細(xì)講解了MediaCodec的使用,需要的可以參考一下2022-04-04
使用kotlin實(shí)現(xiàn)MVP的方式(簡單好用)
這篇文章主要介紹了使用kotlin實(shí)現(xiàn)MVP的方式(簡單好用),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03
Android實(shí)現(xiàn)QQ新用戶注冊(cè)界面遇到問題及解決方法
這篇文章主要介紹了Android實(shí)現(xiàn)QQ新用戶注冊(cè)界面遇到問題及解決方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09

