国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android DataBinding的官方雙向綁定示例

2019-10-22 18:25:08
字體:
來源:轉載
供稿:網友

Android Studio 2.1 Preview 3之后,官方開始支持雙向綁定了。

可惜目前Google并沒有在Data Binding指南里面加入這個教程,并且在整個互聯網之中只有這篇文章介紹了如何使用反向綁定。

在閱讀一下文章之前,我假設你已經知道如何正向綁定。

回顧一下Data Binding

在正向綁定中,我們在Layout里面的綁定表達式是這樣的:

<layout ...> <data>  <variable type="com.example.myapp.User" name="user"/> </data> <RelativeLayout ...>  <TextView android:text="@{user.name}" .../> </RelativeLayout></layout>

當user.name的數據改動時,我們的TextView都會同步改變文字。

雙向綁定

現在假設一種情況,當你更換成EditText時,如果你的用戶名User.name已經綁定到EditText中,當用戶輸入文字的時候,你原來的user.name數據并沒有同步改動,因此我們需要修改成:

<layout ...> <data>  <variable type="com.example.myapp.User" name="user"/> </data> <RelativeLayout ...>  <EditText android:text="@={user.name}" .../> </RelativeLayout></layout>

看出微小的差別了嗎?對,就是"@{}"改成了"@={}",是不是很簡單?

隱式引用屬性

同樣你也可以在別的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>

當CheckBox的狀態發生改變的時候,ImageView也會同時發生改變。在復雜情況下,這個特性沒什么卵用,因為邏輯部分我們是不建議寫在XML中。

如何開啟雙向綁定

開啟雙向綁定,需要在項目的build.gradle中設置:

classpath 'com.android.tools.build:gradle:2.1.0-alpha3'

同樣,你需要在你Module的build.gradle中設置:

android {  ...  dataBinding.enabled = true}

貌似還有點問題

我們剛才的例子里面只顯示了系統自帶的應用,那么如果是自定義控件,或者是我們更細顆粒度的Observable呢?等下就揭曉如何自定義自己的雙向綁定,我們來看看目前Android支持的控件:

  1. AbsListView android:selectedItemPosition
  2. CalendarView android:date
  3. CompoundButton android:checked
  4. DatePicker android:year, android:month, android:day
  5. NumberPicker android:value
  6. RadioGroup android:checkedButton
  7. RatingBar android:rating
  8. SeekBar android:progress
  9. TabHost android:currentTab (估計沒人用)
  10. TextView android:text
  11. TimePicker android:hour, android:minute

自定義雙向綁定

設想一下我們使用了下拉刷新SwipeRefreshLayout控件,這個時候我們希望在加載數據的時候能控制refreshing的狀態,所以我們加入了ObservableBoolean的變量swipeRefreshViewRefreshing來正向綁定數據,并且能夠在用戶手動下拉刷新的時候同步更新swipeRefreshViewRefreshing數據:

// SwipeRefreshLayout.javapublic 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都不是必須的,因為系統會自動生成,寫出來是為了更好地了解如何綁定的,可以參考官方文檔InverseBindingMethod

當然你也可以使用另外一種寫法,并且如果你的值并不是直接對應Observable的值的時候,就可以在這里進行轉換:

@InverseBindingAdapter(attribute = "refreshing", event = "refreshingAttrChanged")public static boolean isRefreshing(SwipeRefreshLayout view) {  return view.isRefreshing();}

上面的event同樣也不是必須的。以上的定義都是為了讓我們能夠在布局文件中使用"@={}"這個雙向綁定的特性。接下來你需要告訴框架如何處理refreshingAttrChanged事件,就像處理一般的監聽事件一樣:

@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();      }    });  }}

一般情況下,我們都需要設置正常的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);}

現在我們終于可以使用雙向綁定的技術啦。但是要注意,需要設置requireAll = false,否則系統將識別不了refreshingAttrChanged屬性,前文提到的文章例子里并沒有設置這個。

在ViewModel中,我們的數據是這樣的:

// MyViewModel.javapublic final ObservableBoolean swipeRefreshViewRefreshing = new ObservableBoolean(false);public void load() {  swipeRefreshViewRefreshing.set(true);  // 網絡請求  ....  swipeRefreshViewRefreshing.set(false);}public SwipeRefreshLayout.OnRefreshListener onRefreshListener() {  return new SwipeRefreshLayout.OnRefreshListener() {    @Override    public void onRefresh() {      // Do something you need    }  };}

在布局文件中是這樣設置的:

<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>

最后我們還有一個小問題,就是雙向綁定有可能會出現死循環,因為當你通過Listener反向設置數據時,數據也會再次發送事件給View。所以我們需要在設置一下避免死循環:

@BindingAdapter("refreshing")public static void setRefreshing(SwipeRefreshLayout view, boolean refreshing) {  if (refreshing != view.isRefreshing()) {    view.setRefreshing(refreshing);  }}

這樣就沒問題啦。以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 灵璧县| 德清县| 巴楚县| 康平县| 丁青县| 临颍县| 宣城市| 亚东县| 铜山县| 苏州市| 安远县| 梓潼县| 阜平县| 东辽县| 林西县| 石城县| 双辽市| 天津市| 什邡市| 浦县| 尼玛县| 沾益县| 竹北市| 大理市| 夹江县| 尚义县| 新丰县| 正镶白旗| 深水埗区| 任丘市| 泰顺县| 年辖:市辖区| 新沂市| 宾川县| 清徐县| 五河县| 察隅县| 化德县| 麻江县| 交口县| 肥城市|