日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

谷歌推荐Data Binding实现MVVM模式(完整文档)

發布時間:2024/4/15 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谷歌推荐Data Binding实现MVVM模式(完整文档) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Data Binding 類庫

這篇文檔將教你如何運用 Data Binding 類庫來編寫聲明試布局,并且盡量減少粘合代碼對你的應用邏輯和布局上的綁定。
Data Binding 是一種靈活和廣泛兼容的類庫,它是一個支持庫,因此你可以在任何 Android 2.1(API level 7+) 以上的設備 使用。
為了使用 Data Binding,Android Gradle 插件版本必須為 1.5.0-alpha1 或以上,查看 如何升級你的 Gradle 插件。

贈送源碼:GitHub - Pangu-Immortal/MagicWX: 🔥免root實現 Android改機(一鍵新機)技術解密,微信無限多開等。。

《最完整的Android逆向知識體系》

構建環境

為了獲取 Data Binding,去 Android SDK manager 下載 它的支持庫。
在你的應用 module 的 build.gradle 添加 dataBinding 來讓你的應用支持 Data Binding。
用以下代碼片段來配置 Data Binding:

android {....//AS4 以上版本使用這個配置dataBindingbuildFeatures {dataBinding true} }

若你有一個應用 module 用了一個依賴了 Data Binding 的類庫,也一樣要在該 module 中配置開啟 Data Binding。
另外,如果想使用 Data Binding,你們你的 Android Studio 版本必須等于或大于 1.3。


Data Binding 布局文件

編寫你的第一個 Data Binding 表達式

Data Binding 的布局文件有一點不一樣,它以 layout 標簽作為根標簽,并且有一個data 元素和 一個 view 元素作為子標簽,這個 view 元素就是你沒有使用 Data Binding 時該有的布局文件。以下是一個例子:

<?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><LinearLayoutandroid: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>

data 標簽下的 variable 是你在這個 Data Binding 布局文件中有可能使用到的對象。

<variable name="user" type="com.example.User"/>

布局中使用 @{} 語法來包裹 variable 中的對象屬性,在下面例子中,TextView 的 text 屬性的值用 user 的 firstName 屬性來替代。

<TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.firstName}"/>

數據對象

現在讓我們假設你有一個普通的 Java 對象(POJO)User:

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

這個對象屬性(final 修飾)是不可變的,如果你的數據對象只提供只讀權限并且之后不會再去修改的話,這種做法很普遍。我們也可以用 JavaBeans 對象來表示:

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

從數據綁定的角度來看,這兩個類是等價的。TextView 的 android:text 屬性值會通過表達式 @{user.firstName} 來獲取第一個類中的 fistName 字段值,活著獲取第二個類中的 getFirstName() 方法返回的值。另外,如果 firstName() 方法存在的話也是可以獲取到值的。

綁定數據

默認情況下,將根據布局文件的名稱生成一個綁定類,將其轉換為 Pascal 格式并將 Binding 作為其后綴。上面的布局文件是名稱
main_activity.xml ,因此生成的綁定類是 MainActivityBinding。這個類將布局屬性(例如用戶變量)綁定到布局的視圖中,并知道如何通過表達式來賦值。創建綁定類的最簡單方式是在視圖 inflate 的時候:

@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);User user = new User("Test", "User");binding.setUser(user); }

完成了!運行這個應用,你會在界面中看到測試的 User。另外,你可以通過一些方
式獲取綁定類:

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

如果你在 ListView 或者 RecyclerView 中使用數據綁定電話,你可以通過一些方式獲取綁定類:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

事件處理

數據綁定允許你編寫表達式來處理從視圖中分派的事件(例如 onClick)。除少數例外,事件屬性名稱由偵聽器中的方法名稱來確定。例如,View.OnLongClickListener 有一個 onLongClick()方法,所以這個事件的屬性是 android:onLongClick。有以下兩種方式來處理一個事件。

  • 方法引用:在表達式中,可以引用符合偵聽器方法簽名的方法。 當表達式被評估為方法引用時,數據綁定將方法引用和所有者對象包裝在偵聽器中,并將該偵聽器設置在目標視圖上。 如果表達式被評估為 null,則數據綁定不會創建偵聽器,而是設置一個空的偵聽器。
  • 監聽器綁定:當事件發生時,lambda 表達式將被評估。 數據綁定總是會在視圖上創建一個監聽器。 當事件被發送時,監聽器將評估 lambda 表達式。

方法引用

事件可以直接綁定到處理的方法中,類似于 android:onClick 可以作為 Activity 的一個方法一樣。與 View#onClick 屬性相比,一個主要的優點是表達式在編譯時被處理,因此如果方法不存在或者它的簽名不正確,就會收到編譯時錯誤。

方法引用和監聽器綁定的主要區別在于實際的監聽器實現是在綁定數據時創建的,而不是在事件觸發時創建的。

要將事件分配給其處理程序,請使用常規綁定表達式,其值是要調用的方法名稱。 例如,如果你的數據對象有兩個方法:

public class MyHandlers {public void onClickFriend(View view) { ... } }

綁定表達式可以為 View 分配一個點擊監聽器:

<?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><LinearLayoutandroid: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>

請注意,表達式中方法的簽名必須與監聽器對象中方法的簽名完全匹配。

監聽器綁定

監聽器綁定是事件發生時運行的綁定表達式。類似于方法引用,但是允許你運行任意的數據綁定表達式。 此功能適用于 Gradle 2.0 版及更高版本的 Android Gradle 插件。

在方法引用中,方法的參數必須與事件偵聽器的參數匹配。 在監聽器綁定中,只有你的返回值必須與監聽器的期望返回值相匹配(除非它返回值為 void )。 例如,您可以有一個具有以下方法的 Presenter 類:

public class Presenter {public void onSaveClick(Task task){} }

然后你可以綁定你的點擊事件到你的類中,例如:

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

監聽器僅可以允許用 lambda 表達式作為根元素。 當表達式中有回調時,數據綁定會自動為事件創建必要的偵聽器和注冊表。 當視圖觸發事件時,數據綁定將評估給定的表達式。 就像在常規的綁定表達式一樣,當這些監聽器表達式被評估的時候,你仍然可以獲取數據綁定的空值和保證線程安全。

請注意,在上面的例子中,我們沒有定義傳入 onClick(android.view.View) 的視圖參數。 監聽器綁定為監聽器參數提供了兩個選擇:您可以忽略該方法的所有參數或將其全部命名。 如果您想要命名參數,則可以在表達式中使用它們。 例如,上面的表達式可以寫成:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

或者如果你想使用表達式中的參數,可以像下面這樣:

public class Presenter {public void onSaveClick(View view, Task task){} } android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

你可以在 lambda 表達式中使用多個參數:

public class Presenter {public void onCompletedChanged(Task task, boolean completed){} } <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果正在偵聽的事件返回值不是 void,則表達式必須返回相同類型的值。 例如,如果你想監聽長按事件,你的表達式應該返回布爾值。

public class Presenter {public boolean onLongClick(View view, Task task){} } android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果由于空對象而無法評估表達式,Data Binding 將返回該類型的默認 Java 值。 例如,引用類型為 null,int 為 0,boolean 為false 等等。

如果您需要使用帶謂詞的表達式(例如三元),則可以使用 void 作為符號。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

避免復雜的監聽器

監聽器表達式非常強大,可以讓你的代碼變得非常容易閱讀。 另一方面,包含復雜表達式的監聽器也會使您的布局難以閱讀和維護。這些表達式應該像從 UI 中傳遞可用數據到回調方法一樣簡單。你應該從偵聽器表達式調用的回調方法內實現業務邏輯。
存在一些專門的單擊事件處理程序,它需要除 android:onClick 之外的其他屬性以避免沖突。 已創建了以下屬性以避免這種沖突:

ClassListener SetterAttribute
SearchViewsetOnSearchClickListener(View.OnClickListener)android:onSearchClick
ZoomControlssetOnZoomInClickListener(View.OnClickListener)android:onZoomIn
ZoomControlssetOnZoomOutClickListener(View.OnClickListener)android:onZoomOut

布局文件細節

Imports

數據元素內可以使用零個或多個 import 元素。 這些就像在 Java 中一樣可以輕松地引用類到你的布局文件中。

<data><import type="android.view.View"/> </data>

現在 View 類可以在你的綁定表達式中使用了。

<TextViewandroid:text="@{user.lastName}"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

如果類名有沖突的話,其中一個類則需起別名了。

<import type="android.view.View"/> <import type="com.example.real.estate.View" alias="Vista"/>

現在,在布局文件中,Vista 被當作 com.example.real.estate.View 引入,View 被當作 android.view.View 引入。 導入的類型可以用作變量和表達式中的類型引用:

<data><import type="com.example.User"/><import type="java.util.List"/><variable name="user" type="User"/><variable name="userList" type="List&lt;User&gt;"/> </data>

注意:Android Studio 尚未處理導入,因此自動導入變量在你的的 IDE 中可能無法完成。 你的應用程序仍然可以正常編譯,你可以通過在變量定義中使用完全限定的名稱來解決 IDE 的這個問題。

<TextViewandroid:text="@{((User)(user.connection)).lastName}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

當在表達式中引用靜態字段和方法時,也可以使用導入的類型:

<data><import type="com.example.MyStringUtils"/><variable name="user" type="com.example.User"/> </data> … <TextViewandroid:text="@{MyStringUtils.capitalize(user.lastName)}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

就像在 Java 文件中一樣,java.lang.* 會被自動導入。

Variables

data 元素內可以使用任意的 variable。 每個變量表示可以在布局中設置的屬性,以用于布局文件中的綁定表達式。

<data><import type="android.graphics.drawable.Drawable"/><variable name="user" type="com.example.User"/><variable name="image" type="Drawable"/><variable name="note" type="String"/> </data>

變量類型在編譯時被檢查,所以如果一個變量實現了 Observable 或者一個 observable collection,那么它應該被反映在類型中。 如果變量是沒有實 Observable 接口的基類或接口,那么它將不會被觀察!

當不同的配置(例如橫向或縱向)有不同的布局文件時,變量將被合并。 這些布局文件之間不得有沖突的變量定義。

生成的綁定類將為每個描述的變量設置一個 setter 和 getter 方法。 變量將采用默認的 Java 值,直到調用 setter 為止 。對于引用類型為 null,對于 int 為0,對于 boolean 為 false 等。

自定義綁定類的名字

默認情況下,根據布局文件的名稱生成一個綁定類,以大寫字母開頭,刪除下劃線(_)并之后的單詞首字母大寫,然后添加后綴 Binding。 這個類將被放置在模塊包下的數據綁定包中。 例如,布局文件 contact_item.xml 將生成 ContactItemBinding。 如果模塊包是 com.example.my.app,那么它將被放置在 com.example.my.app.databinding 中。

綁定類可以通過調整 data 元素的 class 屬性來重命名或放置在不同的包中。 例如:

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

這會在模塊包中的數據綁定包中生成綁定類 ContactItem。 如果該類應該在模塊包中的其他包中生成,則可以用“.”作為前綴:

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

在這種情況下,直接在模塊包中生成了 ContactItem。 如果提供完整的包,則可以使用任意的包:

<data class="com.example.ContactItem">... </data>

Includes

通過在屬性中使用應用程序命名空間和變量名稱,變量可以從包含的布局中傳遞到包含的布局的綁定中:

<?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><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><include layout="@layout/name"bind:user="@{user}"/><include layout="@layout/contact"bind:user="@{user}"/></LinearLayout> </layout>

在這里,name.xml 和 contact.xml 布局文件中都必須有一個 user 變量。

數據綁定不支持 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><include layout="@layout/name"bind:user="@{user}"/><include layout="@layout/contact"bind:user="@{user}"/></merge> </layout>

表達式語言

共同特征

表達式語言看起來很像 Java 表達式。 這些是一樣的:

  • Mathematical + - / * %
  • String concatenation +
  • Logical && ||
  • Binary & | ^
  • Unary + - ! ~
  • Shift >> >>> <<
  • Comparison == > < >= <=
  • instanceof
  • Grouping ()
  • Literals - character, String, numeric, null
  • Cast
  • Method calls
  • Field access
  • Array access []
  • Ternary operator ?:
    例如:
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}'

缺少的操作

你在 Java 中使用的一些表達式語法并不支持綁定操作。

  • this
  • super
  • new
  • 明確的泛型調用

空的合并運算符

空合并運算符 ?? 會選擇左邊的運算結果(如果它不是 null 的話)或右邊的運算結果(如果它是 null 的話)。

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

這在功能上等同于:

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

屬性引用

當一個表達式引用一個類的屬性時,它對字段,setter 和 ObservableFields 使用相同的格式。

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

避免空指針異常

生成的數據綁定代碼會自動檢查空值并避免空指針異常。 例如,在表達式 @ {user.name} 中,如果 user 為 null,則 user.name 將被分配其默認值(null)。 如果引用 user.age,其中age是一個 int,那么它將默認為0。

集合

通用的集合:數組,列表,SparseArray ,map,可以使用 [] 運算符來方便地訪問。

<data><import type="android.util.SparseArray"/><import type="java.util.Map"/><import type="java.util.List"/><variable name="list" type="List&lt;String&gt;"/><variable name="sparse" type="SparseArray&lt;String&gt;"/><variable name="map" type="Map&lt;String, String&gt;"/><variable name="index" type="int"/><variable name="key" type="String"/> </data> … android:text="@{list[index]}" … android:text="@{sparse[index]}" … android:text="@{map[key]}"

字符串文本

在屬性值兩邊使用單引號時,則表達式中使用雙引號:

android:text='@{map["firstName"]}'

也可以使用雙引號來包圍屬性值。 這樣做時,字符串文字應該使用單引號 ' 或者反引號(`)。

android:text="@{map[`firstName`}" android:text="@{map['firstName']}"

資源

使用正常的語法可以將資源作為表達式的一部分進行訪問:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

格式字符串和復數可以通過提供參數來評估:

android:text="@{@string/nameFormat(firstName, lastName)}" android:text="@{@plurals/banana(bananaCount)}"

當一個復數有多個參數時,所有參數都應該傳遞:

Have an orangeHave %d orangesandroid:text="@{@plurals/orange(orangeCount, orangeCount)}"

一些資源需要明確的類型評估:

TypeNormal ReferenceExpression Reference
String[]@array@stringArray
int[]@array@intArray
TypedArray@array@typedArray
Animator@animator@animator
StateListAnimator@animator@stateListAnimator
color int@color@color
ColorStateList@color@colorStateList

Data Objects

任何普通的舊 Java 對象(POJO)都可以用于數據綁定,但修改 POJO 不會導致 UI
更新。 數據綁定的真正威力在于通過給你的數據對象在數據改變時提供通知。 有三種不同的數據更改通知機制,Observable objects, observable fields, observable collections.

當這些可觀察的數據對象被綁定到 UI,并且數據對象的屬性改變時,UI 將被自動更新。

Observable Objects

實現 Observable 接口的類將允許綁定單個偵聽器附加到綁定對象,以偵聽該對象上所有屬性的更改。

Observable 接口具有添加和刪除偵聽器的功能,但通知是由開發者決定的。 為了簡化開發,創建了基類 BaseObservable,以實現偵聽器注冊機制。 數據類實現者仍然負責通知屬性的更改。 這是通過給 getter 分配一個 Bindable 注解并通知 setter 來完成的。

private static class User extends BaseObservable {private String firstName;private String lastName;@Bindablepublic String getFirstName() {return this.firstName;}@Bindablepublic 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);} }

Bindable 注解在編譯期間在 BR 類中生成一個條目。 BR 類文件將在模塊包中生成。 如果數據類的基類沒有改變,Observable 接口可以使用方便的 PropertyChangeRegistry 來實現,以有效地存儲和通知監聽器。

ObservableFields

創建 Observable 類需要做一點工作,所以想要節省時間或擁有很少屬性的開發人員可以使用 ObservableField 及其同胞 ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDoubl 和 ObservableParcelable。 ObservableFields 是具有單個字段的獨立的可觀察對象。 原始版本在訪問操作期間避免裝箱和取消裝箱。 要使用,請在數據類中創建一個公共 final 字段:

private static class User {public final ObservableField<String> firstName =new ObservableField<>();public final ObservableField<String> lastName =new ObservableField<>();public final ObservableInt age = new ObservableInt(); }

就是這樣!要訪問該值,請使用 set 和 get 方法訪問:

user.firstName.set("Google"); int age = user.age.get();

Observable Collections

一些應用程序使用更多的動態結構來保存數據,觀察集合允許對這些數據對象進行鍵值訪問。當鍵是引用類型(如 String)時,ObservableArrayMap 非常有用。

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); user.put("firstName", "Google"); user.put("lastName", "Inc."); user.put("age", 17);

在布局文件中,map 通過字符串鍵來訪問:

<data><import type="android.databinding.ObservableMap"/><variable name="user" type="ObservableMap&lt;String, Object&gt;"/> </data> … <TextViewandroid:text='@{user["lastName"]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/> <TextViewandroid:text='@{String.valueOf(1 + (Integer)user["age"])}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>

當鍵是整形是,可以使用 ObservableArrayList:

ObservableArrayList<Object> user = new ObservableArrayList<>(); user.add("Google"); user.add("Inc."); user.add(17);

在布局中,列表可以通過索引來訪問:

<data><import type="android.databinding.ObservableList"/><import type="com.example.my.app.Fields"/><variable name="user" type="ObservableList&lt;Object&gt;"/> </data> … <TextViewandroid:text='@{user[Fields.LAST_NAME]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/> <TextViewandroid:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>

生成綁定類

生成的綁定類將布局變量與布局中的視圖鏈接起來。 如前所述,綁定的名稱和包可能是自定義的。 生成的綁定類都擴展了 ViewDataBinding。

創建

應該在 inflate 之后立即創建綁定,以確保 View 層次結構不受干擾。 有幾種方法可以綁定到布局。 最常見的是在綁定類中使用靜態方法。inflate 方法 inflate View 層次結構,一步到位。 有一個更簡單的版本,只需要一個 LayoutInflater 和一個 ViewGroup:

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater); MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

如果布局使用不同的機制 inflate,它可能會被分開綁定:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時綁定不能預先知道。 在這種情況下,可以使用 DataBindingUtil 類創建綁定:

ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId, parent, attachToParent); ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

Views With IDs

將在布局中為每個視圖生成一個公開的 final 字段。 該綁定在視圖層次結構上執行單個傳遞,提取帶有 ID 的視圖。 這個機制可以比調用多個視圖的 findViewById 更快。 例如:

<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variable name="user" type="com.example.User"/></data><LinearLayoutandroid: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>

會生成帶有一下字段的綁定類:

public final TextView firstName; public final TextView lastName;

IDs 不像沒有數據綁定那樣必要,但是仍然有一些情況下代碼需要訪問視圖。

變量

每個變量將被賦予訪問器方法。

<data><import type="android.graphics.drawable.Drawable"/><variable name="user" type="com.example.User"/><variable name="image" type="Drawable"/><variable name="note" type="String"/> </data>

會在綁定類中生成 setter 和 getter 方法:

public abstract com.example.User getUser(); public abstract void setUser(com.example.User user); public abstract Drawable getImage(); public abstract void setImage(Drawable image); public abstract String getNote(); public abstract void setNote(String note);

ViewStubs

ViewStub 與普通視圖有點不同。 他們從不可見的時候開始,當他們要么變得可見時,要么被明確告知 inflate 時,他們通過 inflate 另一個布局來取代布局。

由于 ViewStub 本質上從視圖層次中消失,所以綁定對象中的視圖也必須消失以允許收集。 因為視圖是 final 的,所以 ViewStubProxy 對象代替了ViewStub,當 ViewStub 存在時,開發人員可以訪問 ViewStub,并且在 ViewStub被 inflate 時也可以訪問被 inflate 的視圖。

當 inflate 另一個布局時,必須為新的布局建立綁定。因此,ViewStubProxy 必須偵聽 ViewStub 的 ViewStub.OnInflateListener 并在此時建立綁定。由于只有一個可以存在,ViewStubProxy 允許開發者在建立綁定之后設置一個 OnInflateListener 對象。

高級綁定

動態變量

有時,特定的綁定類將不被知道。 例如,針對任意布局的 RecyclerView.Adapter 將不知道具體的綁定類。 它仍然必須在 onBindViewHolder(VH,int) 期間分配綁定值。

在這個例子中,RecyclerView 綁定的所有布局都有一個 item 變量。BindingHolder 有一個返回 ViewDataBinding 基類的 getBinding 方法。

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

立即綁定

當變量或 observable 變化時,綁定將被安排在下一幀之前改變。但有時候,綁定必須立即執行。要強制執行,請使用 executePendingBindings() 方法。

后臺線程

只要不是集合,就可以在后臺線程中更改數據模型。數據綁定將在評估時本地化每個變量/字段,以避免任何并發問題。


屬性設置

每當綁定值發生變化時,生成的綁定類必須使用綁定表達式在視圖上調用setter方法。 數據綁定框架可以自定義調用哪個方法來設置值。

自動的設置器

對于一個屬性,數據綁定將試圖找到設置屬性的方法。屬性的命名空間并不重要,只有屬性名稱本身才重要。例如,與 TextView 的屬性 android:text 相關聯的表達式將查找 setText(String)。 如果表達式返回 int,那么數據綁定將搜索一個 setText(int) 方法。請注意讓表達式返回正確的類型,如果需要的話就進行轉換。即使給定名稱不存在任何屬性,數據綁定也可以工作。 然后,您可以使用數據綁定輕松地為任何 setter 創建屬性。 例如,support 庫中的 DrawerLayout 沒有任何屬性,但是有很多 setter。 您可以使用自動設置器來使用其中的一個。

<android.support.v4.widget.DrawerLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:scrimColor="@{@color/scrim}"app:drawerListener="@{fragment.drawerListener}"/>

重命名設置器

一些屬性的設置器會與名稱不匹配。 對于這些方法,一個屬性可能通過 BindingMethods 注解與設置器關聯。 這必須與一個類相關聯,每個重命名的方法一個包含一個 BindingMethod 注解。例如,android:tint 屬性確實與 setImageTintList(ColorStateList) 關聯,而不是 setTint。

@BindingMethods({@BindingMethod(type = "android.widget.ImageView",attribute = "android:tint",method = "setImageTintList"), })

開發人員不太可能需要重命名設置器, 安卓框架已經為這些屬性實現了。

自定義設置器

一些屬性需要自定義綁定邏輯。 例如,android:paddingLeft 屬性沒有關聯的設置器。 相反,setPadding(eft, top, right, bottom) 存在。 使用 BindingAdapter 注釋的靜態綁定適配器方法允許開發人員自定義如何調用屬性的設置器。

安卓屬性已經創建了 BindingAdapters。 例如,這里是 paddingLeft:

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

綁定適配器對其他類型的自定義非常有用。 例如,一個自定義的加載器可以被調用脫機線程來加載一個圖像。當發生沖突時,開發人員創建的綁定適配器將覆蓋數據綁定默認適配器。您也可以讓適配器接收多個參數。

@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); } <ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}"/>

如果 imageUrl 和 error 都用于 ImageView 且 imageUrl 是字符串,并且 error 是 drawable,則將調用此適配器。
自定義名稱空間在匹配過程中被忽略。
也可以為 android 命名空間編寫適配器。
綁定適配器方法可以選擇在其處理程序中使用舊值。 采用新舊值的方法,應該把屬性的所有舊的值放在第一位,然后是新的值:

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

當一個監聽器有多個方法時,它必須被分成多個監聽器。例如,View.OnAttachStateChangeListener 有兩個方法:onViewAttachedToWindow() 和 onViewDetachedFromWindow()。然后我們必須創建兩個接口來區分它們的屬性和處理器。

@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewDetachedFromWindow {void onViewDetachedFromWindow(View v); }@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewAttachedToWindow {void onViewAttachedToWindow(View v); }

因為更改一個偵聽器也會影響另一個偵聽器,所以我們必須有三個不同的綁定適配器,一個用于每個屬性,另一個用于兩個,它們都應該被設置。

@BindingAdapter("android:onViewAttachedToWindow") public static void setListener(View view, OnViewAttachedToWindow attached) {setListener(view, null, attached); }@BindingAdapter("android:onViewDetachedFromWindow") public static void setListener(View view, OnViewDetachedFromWindow detached) {setListener(view, detached, null); }@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"}) public static void setListener(View view, final OnViewDetachedFromWindow detach,final OnViewAttachedToWindow attach) {if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {final OnAttachStateChangeListener newListener;if (detach == null && attach == null) {newListener = null;} else {newListener = new OnAttachStateChangeListener() {@Overridepublic void onViewAttachedToWindow(View v) {if (attach != null) {attach.onViewAttachedToWindow(v);}}@Overridepublic void onViewDetachedFromWindow(View v) {if (detach != null) {detach.onViewDetachedFromWindow(v);}}};}final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,newListener, R.id.onAttachStateChangeListener);if (oldListener != null) {view.removeOnAttachStateChangeListener(oldListener);}if (newListener != null) {view.addOnAttachStateChangeListener(newListener);}} }

上面的例子比正常情況稍微復雜一點,因為視圖對偵聽器使添加和刪除,而不是對 View.OnAttachStateChangeListener 使用set方法。 android.databinding.adapters.ListenerUtil 類有助于跟蹤以前的監聽器,以便它們可以在綁定適配器中被移除。通過使用 @TargetApi(VERSION_CODES.HONEYCOMB_MR1) 注解接口 OnViewDetachedFromWindow 和OnViewAttachedToWindow,數據綁定代碼生成器知道只應在 API 12 或以上的設備上調用 addOnAttachStateChangeListener(View.OnAttachStateChangeListener) 來運行運行偵聽器。


轉換器

對象轉換

從綁定表達式返回一個對象時,將從自動,重命名和自定義的設置器中選擇一個設置器。 該對象將被轉換為所選設置器的參數類型。
這對于那些使用 ObservableMaps 來保存數據的開發者來說是很方便的。例如:

<TextViewandroid:text='@{userMap["lastName"]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>

userMap 返回一個對象,該對象將被自動轉換為在 setText(CharSequence) 中找到的參數類型。 當參數類型可能混淆時,開發者需要在表達式中輸入。

自定義轉換

有時轉換應該在特定類型之間自動進行。 例如,設置 background 時:

<Viewandroid:background="@{isError ? @color/red : @color/white}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

在這里,背景需要一個 Drawable,但是顏色是一個整數。每當一個 Drawable 被判斷該返回一個整數時,該整形應該被轉換成一個 ColorDrawable。 這個轉換是通過一個帶有 BindingConversion 注解的靜態方法完成的:

@BindingConversion public static ColorDrawable convertColorToDrawable(int color) {return new ColorDrawable(color); }

請注意,轉換只發生在設置器級別,所以不允許混合類型,如下所示:

<Viewandroid:background="@{isError ? @drawable/error : @color/white}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

Android Studio 支持數據綁定

Android Studio 支持數據綁定代碼的許多代碼編輯功能。例如,它支持數據綁定表達式的以下功能:

  • 語法高亮
  • 表達式語法錯誤的提示
  • XML代碼完成
  • 包括導航(如導航到聲明)和快速文檔的參考

注意:如果沒有錯誤,則數組和一般類型(如 Observable 類)可能會顯示錯誤。

預覽窗格顯示數據綁定表達式的默認值(如果提供的話)。在以下示例摘錄布局XML文件中的元素時,預覽窗格將在 TextView 中顯示 PLACEHOLDER 默認文本值。

<TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.firstName, default=PLACEHOLDER}"/>

如果需要在項目設計階段顯示默認值,則還可以使用工具屬性而不是默認表達式值,如 Design Time Layout Attributes 中所述。


原文地址:https://developer.android.google.cn/topic/libraries/data-binding/index.html

贈送源碼:GitHub - Pangu-Immortal/MagicWX: 🔥免root實現 Android改機(一鍵新機)技術解密,微信無限多開等。。

《最完整的Android逆向知識體系》

Pangu-Immortal (Pangu-Immortal) · GitHub

總結

以上是生活随笔為你收集整理的谷歌推荐Data Binding实现MVVM模式(完整文档)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。