天天看點

Android Architecture Components(四)DataBinding

DataBinding簡介

官方文檔

The Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically.

DataBinding允許你在layout當中将UI和資料源進行綁定,而不用程式設計的方式綁定。

Binding components in the layout file lets you remove many UI framework calls in your activities, making them simpler and easier to maintain. This can also improve your app’s performance and help prevent memory leaks and null pointer exceptions.

在layout布局中綁定元件可以讓你在activity中移除很多UI操作,使維護更加簡單容易。這也可以提高app的性能,防止記憶體洩漏和空指針異常。

編譯環境

要将應用程式配置為使用資料綁定,請将dataBinding元素添加到app子產品中的build.gradle檔案中,如以下示例所示:

android {
    ...
    dataBinding {
        enabled = true
    }
}
           

Note: You must configure data binding for app modules that depend on libraries that use data binding, even if the app module doesn’t directly use data binding.

注意:您必須為依賴于使用資料綁定的庫的應用程式子產品配置資料綁定,即使應用程式子產品不直接使用資料綁定也是如此

Android Studio對data binding的支援

Android Studio支援許多用于databinding代碼的編輯功能。 例如,它支援資料綁定表達式的以下功能:

文法highlight顯示
标記表達式語言文法錯誤
XML代碼補全
參考資訊,包括導航(例如導航到聲明)和快速文檔
           

警告:數組和泛型類型(如Observable類)可能會錯誤地顯示錯誤。

用于綁定類的新資料綁定編譯器

Android Gradle插件版本3.1.0-alpha06包含一個生成綁定類的新資料綁定編譯器。 新編譯器會逐漸建立綁定類,這在大多數情況下會加快建構過程。 要了解有關綁定類的更多資訊,請參閱生成的綁定類。

布局和綁定表達式

表達式語言允許您編寫處理視圖排程的事件的表達式。資料綁定庫自動生成将布局中的視圖與資料對象綁定所需的類。

資料綁定布局檔案略有不同,以布局的根标簽開頭,後跟資料元素和視圖根元素。此視圖元素是您的根在非綁定布局檔案中的位置。

以下代碼顯示了一個示例布局檔案:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>
           

Note: Layout expressions should be kept small and simple, as they can’t be unit tested and have limited IDE support. In order to simplify layout expressions, you can use custom binding adapters.

使用Bindung Adapters可以自定義綁定。

Data object
public class User {
  public final String firstName;
  public final String lastName;
  public User(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
}

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() {
      return this.firstName;
  }
  public String getLastName() {
      return this.lastName;
  }
}
           

從資料綁定的角度來看,這兩個類是等價的。用于android:text屬性的表達式@ {user.firstName}通路前一類中的firstName字段和後一類中的getFirstName()方法。或者,它也會解析為firstName(),如果該方法存在。

Binding data

預設情況下,類的名稱基于布局檔案的名稱,将其轉換為Pascal大小寫并向其添加Binding字尾。 activity_main.xml相應的生成類是ActivityMainBinding。 此類包含布局屬性(例如,使用者變量)到布局視圖的所有綁定,并知道如何為綁定表達式配置設定值。建立綁定的推薦方法是在擴充布局時執行此操作,如下例所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
   User user = new User("Test", "User");
   binding.setUser(user);
   
or:

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
           

我截取了一段我的工程中生成的DataBinding代碼:

public abstract class MainTagItemBinding extends ViewDataBinding {

  ...

  @NonNull
  public static MainTagItemBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot) {
    return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
  }

  @NonNull
  public static MainTagItemBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot, @Nullable DataBindingComponent component) {
    return DataBindingUtil.<MainTagItemBinding>inflate(inflater, com.pegg.video.R.layout.main_tag_item, root, attachToRoot, component);
  }
  
  ...
}
           
Null coalescing operator

如果前操作數不為空,則空合并運算符(??)選擇左操作數;如果前操作數為空,則選擇右操作數。

android:text="@{user.displayName ?? user.lastName}"

等價于:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
           
事件處理

資料綁定允許您編寫從視圖排程的表達式處理事件(例如,onClick()方法)。事件屬性名稱由偵聽器方法的名稱确定,但有一些例外。 例如,View.OnClickListener有一個方法onClick(),是以該事件的屬性是android:onClick。

click事件有一些專門的事件處理程式需要除android:onClick以外的屬性以避免沖突。 您可以使用以下屬性來避免這些類型的沖突:

Class Listener setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

您可以使用以下機制來處理事件:

Method references: In your expressions, you can reference methods that conform to the signature of the listener method. When an expression evaluates to a method reference, Data binding wraps the method reference and owner object in a listener, and sets that listener on the target view. If the expression evaluates to null, Data binding doesn’t create a listener and sets a null listener instead.

在表達式中,您可以引用符合偵聽器方法簽名的方法。 當表達式求值為方法引用時,Data綁定将方法引用和所有者對象包裝在偵聽器中,并在目标視圖上設定該偵聽器。 如果表達式求值為null,則資料綁定不會建立偵聽器并改為設定空偵聽器。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>
           

Listener bindings: These are lambda expressions that are evaluated when the event happens. Data binding always creates a listener, which it sets on the view. When the event is dispatched, the listener evaluates the lambda expression.

這些是在事件發生時計算的lambda表達式。 資料綁定總是建立一個偵聽器,它在視圖上設定。 排程事件時,偵聽器将計算lambda表達式。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

           

Method references和Listener bindings定之間的主要差別在于實際的偵聽器實作是在綁定資料時建立的,而不是在觸發事件時建立的。 如果您希望在事件發生時評估lambda表達式,則應使用Listener bindings。

Imports, variables, and includes

The Data Binding Library provides features such as imports, variables, and includes. Imports make easy to reference classes inside your layout files. Variables allow you to describe a property that can be used in binding expressions. Includes let you reuse complex layouts across your app.

imports使布局檔案中的類很容易引用。Variables允許您描述可用于綁定表達式的屬性。Includes讓您在整個應用中重複使用複雜的布局。

A special variable named context is generated for use in binding expressions as needed. The value for context is the Context object from the root View’s getContext() method. The context variable is overridden by an explicit variable declaration with that name.

context的值是來自根View的getContext()方法的Context對象。 使用該名稱的顯式變量聲明覆寫上下文變量。

Data binding doesn’t support include as a direct child of a merge element. For example, the following layout isn’t supported:

資料綁定不支援include作為merge元素的直接子元素。 例如,不支援以下布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <merge><!-- Doesn't work -->
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>
           
Work with observable data objects

Any plain-old object can be used for data binding, but modifying the object doesn’t automatically cause the UI to update. Data binding can be used to give your data objects the ability to notify other objects, known as listeners, when its data changes. There are three different types of observable classes: objects, fields, and collections.

任何普通對象都可用于資料綁定,但修改對象不會自動導緻UI更新。 資料綁定可用于為資料對象提供在資料更改時通知其他對象(稱為偵聽器)的能力。 有三種不同類型的可觀察類:對象,字段和集合。

When one of these observable data objects is bound to the UI and a property of the data object changes, the UI is updated automatically.

當其中一個可觀察資料對象綁定到UI并且資料對象的屬性發生更改時,UI将自動更新。

The Observable interface has a mechanism to add and remove listeners, but you must decide when notifications are sent. To make development easier, the Data Binding Library provides the BaseObservable class, which implements the listener registration mechanism. The data class that implements BaseObservable is responsible for notifying when the properties change. This is done by assigning a Bindable annotation to the getter and calling the notifyPropertyChanged() method in the setter, as shown in the following example:

Observable接口有一個添加和删除偵聽器的機制,但您必須決定何時發送通知。 為了簡化開發,資料綁定庫提供了BaseObservable類,該類實作了偵聽器注冊機制。 實作BaseObservable的資料類負責通知屬性何時更改。 這是通過将一個Bindable注釋配置設定給getter并在setter中調用notifyPropertyChanged()方法來完成的,如以下示例所示:

private static class User extends BaseObservable {
    private String firstName;
    private String lastName;

    @Bindable
    public String getFirstName() {
        return this.firstName;
    }

    @Bindable
    public String getLastName() {
        return this.lastName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}
           

資料綁定在子產品包中生成一個名為BR的類,該類包含用于資料綁定的資源的ID。 Bindable注釋在編譯期間在BR類檔案中生成一個條目。 如果無法更改資料類的基類,則可以使用PropertyChangeRegistry對象實作Observable接口,以有效地注冊和通知偵聽器。

生成的綁定類

資料綁定庫生成用于通路布局的變量和視圖的綁定類。 此頁面顯示如何建立和自定義生成的綁定類。

生成的綁定類将布局變量與布局中的視圖連結起來。 綁定類的名稱和包可以自定義。 所有生成的綁定類都繼承自ViewDataBinding類。

預設情況下,類的名稱基于布局檔案的名稱,将其轉換為Pascal大小寫并向其添加Binding字尾。 上面的布局檔案名是activity_main.xml,是以相應的生成類是ActivityMainBinding。 此類包含布局屬性(例如,使用者變量)到布局視圖的所有綁定,并知道如何為綁定表達式指定值。

Views with IDs

The Data Binding Library creates a immutable field in the binding class for each view that has an ID in the layout. For example, the Data Binding Library creates the firstName and lastName fields of type TextView from the following layout:

資料綁定庫在綁定類中為每個在布局中具有ID的視圖建立不可變字段。 例如,資料綁定庫從以下布局建立TextView類型的firstName和lastName字段:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
   android:id="@+id/firstName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
  android:id="@+id/lastName"/>
   </LinearLayout>
</layout>
           

The library extracts the views including the IDs from the view hierarchy in a single pass. This mechanism can be faster than calling the findViewById() method for every view in the layout.

該庫在一次傳遞中從視圖層次結構中提取包括ID的視圖。 此機制比為布局中的每個視圖調用findViewById()方法更快。

IDs aren’t as necessary as they are without data binding, but there are still some instances where access to views is still necessary from code.

加載沒有資料綁定的控件的ID沒有必要,但仍有一些情況需要從代碼通路視圖。

ViewStubs

與普通視圖不同,ViewStub對象從一個不可見的視圖開始。 當它們被顯示或被明确告知要inflate時,它們會通過inflate另一個布局來替換自己的布局。

由于ViewStub基本上從視圖層次結構中消失,是以綁定對象中的視圖也必須消失以允許通過垃圾回收聲明。 因為視圖是final的,是以ViewStubProxy對象取代了生成的綁定類中的ViewStub,使您可以在ViewStub存在時通路它,并在ViewStub inflate時通路inflate的視圖層次結構。

在inflate另一個布局時,必須為新布局建立綁定。 是以,ViewStubProxy必須偵聽ViewStub OnInflateListener并在需要時建立綁定。 由于在給定時間隻能存在一個偵聽器,是以ViewStubProxy允許您設定OnInflateListener,它在建立綁定後調用它。

動态變量

有時,特定的綁定類是未知的。 例如,針對任意布局操作的RecyclerView.Adapter不知道特定的綁定類。 它仍然必須在調用onBindViewHolder()方法期間配置設定綁定值。

在以下示例中,RecyclerView綁定的所有布局都具有項變量。 BindingHolder對象有一個getBinding()方法傳回ViewDataBinding基類。

public void onBindViewHolder(BindingHolder holder, int position) {
    final T item = mItems.get(position);
    holder.getBinding().setVariable(BR.item, item);
    holder.getBinding().executePendingBindings();
}
           
Background Thread

You can change your data model in a background thread as long as it isn’t a collection. Data binding localizes each variable / field during evaluation to avoid any concurrency issues.

您可以在背景線程中更改資料模型,隻要它不是集合即可。 資料綁定在evaluation期間本地化每個變量/字段以避免任何并發問題。

Custom binding class names

By default, a binding class is generated based on the name of the layout file, starting with an uppercase letter, removing underscores ( _ ), capitalizing the following letter, and suffixing the word Binding. The class is placed in a databinding package under the module package. For example, the layout file contact_item.xml generates the ContactItemBinding class. If the module package is com.example.my.app, then the binding class is placed in the com.example.my.app.databinding package.

預設情況下,将根據布局檔案的名稱生成綁定類,以大寫字母開頭,删除下劃線(_),大寫以下字母,并為單詞Binding添加字尾。 該類放在子產品包下的資料綁定包中。 例如,布局檔案contact_item.xml生成ContactItemBinding類。 如果子產品包是com.example.my.app,則綁定類放在com.example.my.app.databinding包中。

Binding classes may be renamed or placed in different packages by adjusting the class attribute of the data element. For example, the following layout generates the ContactItem binding class in the databinding package in the current module:

通過調整資料元素的class屬性,可以重命名綁定類或将綁定類放在不同的包中。 例如,以下布局在目前子產品的資料綁定包中生成ContactItem綁定類:

<data class="ContactItem">
    …
</data>

<data class="com.example.ContactItem">
    …
</data>
           
Binding adapters

The Data Binding Library allows you to specify the method called to set a value, provide your own binding logic, and specify the type of the returned object by using adapters.

資料綁定庫允許您指定調用的方法來設定值,提供自己的綁定邏輯,并使用擴充卡指定傳回對象的類型。

Specify a custom method name

Some attributes have setters that don’t match by name. In these situations, an attribute may be associated with the setter using the BindingMethods annotation. The annotation is used with a class and can contain multiple BindingMethod annotations, one for each renamed method. Binding methods are annotations that can be added to any class in your app. In the following example, the android:tint attribute is associated with the setImageTintList(ColorStateList) method, not with the setTint() method:

某些屬性具有名稱不比對的setter。 在這些情況下,可以使用BindingMethods注釋将屬性與setter相關聯。 注釋與類一起使用,可以包含多個BindingMethod注釋,每個注釋方法一個注釋。 綁定方法是可以添加到應用程式中任何類的注釋。 在以下示例中,android:tint屬性與setImageTintList(ColorStateList)方法關聯,而不是與setTint()方法關聯:

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})
           
提供自定義邏輯

某些屬性需要自定義綁定邏輯。 例如,android:paddingLeft屬性沒有關聯的setter。 相反,提供了setPadding(左,上,右,下)方法。 使用BindingAdapter注釋的靜态綁定擴充卡方法允許您自定義如何調用屬性的setter。

Android架構類的屬性已經建立了BindingAdapter注釋。 例如,以下示例顯示了paddingLeft屬性的綁定擴充卡:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
  view.setPadding(padding,
                  view.getPaddingTop(),
                  view.getPaddingRight(),
                  view.getPaddingBottom());
}
           

參數類型很重要。 第一個參數确定與屬性關聯的視圖的類型。 第二個參數确定給定屬性的綁定表達式中接受的類型。

綁定擴充卡可用于其他類型的自定義。 例如,可以從工作線程調用自定義加載程式來加載圖像。

當發生沖突時,您定義的綁定擴充卡将覆寫Android架構提供的預設擴充卡。

您還可以使用接收多個屬性的擴充卡,如以下示例所示:

@BindingAdapter({"imageUrl", "error"})
public static void loadImage(ImageView view, String url, Drawable error) {
  Picasso.get().load(url).error(error).into(view);
}

<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
           

注意:資料綁定庫會忽略自定義命名空間以進行比對。

如果imageUrl和error都用于ImageView對象并且imageUrl是字元串且error是Drawable,則調用擴充卡。 如果希望在設定任何屬性時調用擴充卡,則可以将擴充卡的可選requireAll标志設定為false,如以下示例所示:

@BindingAdapter(value={"imageUrl", "placeholder"}, requireAll=false)
public static void setImageUrl(ImageView imageView, String url, Drawable placeHolder) {
  if (url == null) {
    imageView.setImageDrawable(placeholder);
  } else {
    MyImageLoader.loadInto(imageView, url, placeholder);
  }
}
           

注意:發生沖突時,綁定擴充卡會覆寫預設資料綁定擴充卡。

綁定擴充卡方法可以選擇在其處理程式中使用舊值。 采用舊值和新值的方法應首先聲明屬性的所有舊值,然後是新值,如下例所示:

@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());
   }
}
           
Use ViewModel to manage UI-related data

資料綁定庫與ViewModel元件無縫協作,ViewModel元件公開布局觀察到的資料并對其更改做出反應。 将ViewModel元件與資料綁定庫一起使用,可以将UI邏輯從布局移動到元件中,這些元件更易于測試。 資料綁定庫可確定在需要時綁定和取消綁定資料源。 剩下的大部分工作都在于確定您公開正确的資料。

要将ViewModel元件與資料綁定庫一起使用,必須執行個體化從Viewmodel類繼承的元件,擷取綁定類的執行個體,并将ViewModel元件配置設定給綁定類中的屬性。 以下示例顯示如何将該元件與庫一起使用:

class ViewModelActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Obtain the ViewModel component.
        UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);

        // Inflate view and obtain an instance of the binding class.
        UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user);

        // Assign the component to a property in the binding class.
        binding.viewmodel = userModel;
    }
}

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@{viewmodel.rememberMe}"
    android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />
           
使用Observable ViewModel可以更好地控制綁定擴充卡

您可以使用實作Observable的ViewModel元件來通知其他應用程式元件有關資料更改的資訊,類似于使用LiveData對象的方式。

在某些情況下,您可能更喜歡使用ViewModel元件來實作Observable接口,而不是使用LiveData對象,即使您丢失了LiveData的生命周期管理功能。使用實作Observable的ViewModel元件可以更好地控制應用程式中的綁定擴充卡。例如,此模式使您可以在資料更改時更好地控制通知,還允許您指定自定義方法以在雙向資料綁定中設定屬性的值。

要實作可觀察的ViewModel元件,必須建立一個繼承自ViewModel類并實作Observable接口的類。當觀察者使用addOnPropertyChangedCallback()和removeOnPropertyChangedCallback()方法訂閱或取消訂閱通知時,您可以提供自定義邏輯。您還可以提供在notifyPropertyChanged()方法中屬性更改時運作的自定義邏輯。以下代碼示例示範如何實作可觀察的ViewModel:

/**
 * A ViewModel that is also an Observable,
 * to be used with the Data Binding Library.
 */
class ObservableViewModel extends ViewModel implements Observable {
    private PropertyChangeRegistry callbacks = new PropertyChangeRegistry();

    @Override
    protected void addOnPropertyChangedCallback(
            Observable.OnPropertyChangedCallback callback) {
        callbacks.add(callback);
    }

    @Override
    protected void removeOnPropertyChangedCallback(
            Observable.OnPropertyChangedCallback callback) {
        callbacks.remove(callback);
    }

    /**
     * Notifies observers that all properties of this instance have changed.
     */
    void notifyChange() {
        callbacks.notifyCallbacks(this, 0, null);
    }

    /**
     * Notifies observers that a specific property has changed. The getter for the
     * property that changes should be marked with the @Bindable annotation to
     * generate a field in the BR class to be used as the fieldId parameter.
     *
     * @param fieldId The generated BR id for the Bindable field.
     */
    void notifyPropertyChanged(int fieldId) {
        callbacks.notifyCallbacks(this, fieldId, null);
    }
}
           
雙向資料綁定

使用單向資料綁定,您可以在屬性上設定值,并設定對該屬性中的更改作出反應的偵聽器

單向綁定:
<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@{viewmodel.rememberMe}"
    android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
/>

雙向綁定:
<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@={viewmodel.rememberMe}"
/>
           

@ = {}表示法(主要包括“=”符号)接收屬性的資料更改并同時偵聽使用者更新。

為了對支援資料的更改做出反應,您可以使布局變量成為Observable的實作,通常是BaseObservable,并使用@Bindable注釋,如以下代碼片段所示:

public class LoginViewModel extends BaseObservable {
    // private Model data = ...

    @Bindable
    public Boolean getRememberMe() {
        return data.rememberMe;
    }

    public void setRememberMe(Boolean value) {
        // Avoids infinite loops.
        if (data.rememberMe != value) {
            data.rememberMe = value;

            // React to the change.
            saveData();

            // Notify observers of a new value.
            notifyPropertyChanged(BR.remember_me);
        }
    }
}
           
使用自定義屬性進行雙向資料綁定

對自定義屬性使用雙向資料綁定,需要使用@InverseBindingAdapter和@InverseBindingMethod注釋。

例如,如果要在名為MyView的自定義視圖中的“time”屬性上啟用雙向資料綁定,請完成以下步驟:

1.注釋設定初始值的方法,并在使用@BindingAdapter更改值時進行更新:

@BindingAdapter("time")
public static void setTime(MyView view, Time newValue) {
    // Important to break potential infinite loops.
    if (view.time != newValue) {
        view.time = newValue;
    }
}
           

2.使用@InverseBindingAdapter注釋從視圖中讀取值的方法:

@InverseBindingAdapter("time")
public static Time getTime(MyView view) {
    return view.getTime();
}
           

此時,資料綁定知道在資料更改時要做什麼(它調用使用@BindingAdapter注釋的方法)以及在view屬性更改時調用的内容(它調用InverseBindingListener)。但是,它不知道屬性何時或如何更改。

為此,您需要在視圖上設定一個偵聽器。 它可以是與自定義視圖關聯的自定義偵聽器,也可以是通用事件,例如失去焦點或文本更改。 将@BindingAdapter注釋添加到為該屬性的更改設定偵聽器的方法:

@BindingAdapter("app:timeAttrChanged")
public static void setListeners(
        MyView view, final InverseBindingListener attrChange) {
    // Set a listener for click, focus, touch, etc.
}
           
Converters

如果綁定到View對象的變量需要在顯示之前以某種方式進行格式化,翻譯或更改,則可以使用Converter對象。

例如,使用顯示日期的EditText對象:

<EditText
    android:id="@+id/birth_date"
    android:text="@={Converter.dateToString(viewmodel.birthDate)}"
/>
           

viewmodel.birthDate屬性包含Long類型的值,是以需要使用轉換器對其進行格式化。

因為正在使用雙向表達式,是以還需要一個逆轉換器讓庫知道如何将使用者提供的字元串轉換回後備資料類型,在本例中為Long。 此過程通過将@InverseMethod注釋添加到其中一個轉換器并使此注釋引用逆轉換器來完成。 以下代碼段中顯示了此配置的示例:

public class Converter {
    @InverseMethod("stringToDate")
    public static String dateToString(EditText view, long oldValue,
            long value) {
        // Converts long to String.
    }

    public static long stringToDate(EditText view, String oldValue,
            String value) {
        // Converts String to long.
    }
}
           
雙向屬性

當您使用下表中的屬性時,該平台為雙向資料綁定提供内置支援。

Class Attribute(s) Binding adapter
AdapterView android:selectedItemPosition android:selection AdapterViewBindingAdapter
CalendarView android:date CalendarViewBindingAdapter
CompoundButton android:checked CompoundButtonBindingAdapter
DatePicker android:year android:month android:day DatePickerBindingAdapter
NumberPicker android:value NumberPickerBindingAdapter
RadioButton android:checkedButton RadioGroupBindingAdapter
RatingBar android:rating RatingBarBindingAdapter
SeekBar android:progress SeekBarBindingAdapter
TabHost android:currentTab TabHostBindingAdapter
TextView android:text TextViewBindingAdapter
TimePicker android:hour android:minute TimePickerBindingAdapter

源碼分析

本來計劃寫這部分,但是在網上看到了

DataBinding學習筆記(一)源碼分析 author:listen2code 這篇文章,寫的很詳細,而且讀起來特别易懂