授權原文:https://mp.weixin.qq.com/s/n7VuWeZnx_1Ds5guMqsYSA
前言Data Binding是一種支援庫,藉助該庫,您可以使用宣告性格式(而非程式化地)將佈局中的介面元件繫結到應用中的資料來源。
佈局通常是使用呼叫介面框架方法的程式碼在 Activity 中定義的。例如,以下程式碼呼叫 findViewById() 來查詢 TextView 控制元件並將其繫結到 viewModel 變數的 userName 屬性:
TextView textView = findViewById(R.id.sample_text); textView.setText(viewModel.getUserName());
以下示例展示瞭如何在佈局檔案中使用Data Binding 將文字直接分配到TextView。這樣就無需呼叫上述任何 Java 程式碼。請注意賦值表示式中 @{} 語法的使用:
<TextView android:text="@{viewmodel.userName}" />
注意:
如果您使用Data Biding的主要目的是取代 findViewById() 呼叫,請考慮改用ViewBinding。
使用過ButterKnife的都知道,目前ButterKnife作者建議切換至ViewBindng使用;在許多情況下,ViewBinding可簡化實現,提高效能,提供與DataBinding相同的好處。
dataBinding 的優勢雙向資料繫結
資料發生改變後,dataBinding 會自動通知 UI 重新整理頁面,不再需要人工繫結最新資料到 View 上。UI 改變後也能同步給資料。
減少模板程式碼
有了 dataBinding,從此不用再寫 findViewById,setOnClickListener 等枯燥生硬的程式碼,大大提高工作效率。從此 Butterknife 靠邊站。
釋放 Activity/Fragment
以前,我們在 Activity , Fragment 或 Presenter 中計算資料再繫結到 View 元件上,導致 View 層很臃腫,現在這部分工作我們可以直接在 xml 佈局檔案中完成。Activity , Fragment 讓它更加只關注核心業務。
資料繫結空安全
在 xml 中繫結資料它是空安全的,因為 dataBinding 在資料繫結上會自動裝箱和空判斷,所以大大減少了資料繫結帶來的 NullpointException 問題。
在使用 dataBinding 的時候,很多同學會誤以為不方便除錯;
現在的 databinding 在編譯階段也會有豐富的錯誤提示,在執行階段,我們可以根據佈局檔案找到實現類,跟進去斷點排查問題。如fragment_layout_my.xml佈局,在編譯時會生成 FragmentLayoutMyImpl.java 實現類,我們可以搜尋這種類 debug 跟進解決問題。Data Binding使用場景:
Data Binding使用前需要先引入
在app的build.gradle中加上以下程式碼即可,不用引用其他的依賴
android { ... dataBinding { enabled = true }}
佈局和繫結表示式
資料繫結的佈局以根標記 layout 開頭,後跟 data 元素和 view 根元素。如下:
注意:佈局表示式應保持精簡,因為它們無法進行單元測試,並且擁有的 IDE 支援也有限。為了簡化佈局表示式,可以使用自定義繫結介面卡。
1、預設情況下,類名稱基於佈局檔案的名稱,它會轉換為駝峰形式並在末尾新增 Binding 字尾。
2、以上佈局檔名為 activity_main.xml,因此生成的對應類為 ActivityMainBinding,==且都是ViewDataBinding的子類,所有佈局對應的生成的繫結類都可以是ViewDataBinding類==
3、此類包含從佈局屬性(例如,user 變數)到佈局檢視的所有繫結,並且知道如何為繫結表示式指定值。
4、建議的繫結建立方法是在擴充佈局時建立,如以下示例所示:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //此時可以透過DataBindingUtil來設定Activity的頁面佈局。 //此時會返回一個ActivityMainBinding物件。 //這個是編譯時根據xml佈局檔案中的資料繫結自動生成的實現類。 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); //完成資料繫結 binding.setUser(user); }
a、Activity 資料繫結 ( DataBinding ) :
1、DataBindingUtil類方法:
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
2、生成的佈局繫結類的inflate()方法:
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //DataBindingUtil類方法 ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); //生成的佈局繫結類的inflate()方法 //ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater()); User user = new User("Test", "User"); binding.setUser(user); }
b、 Fragment、ListView 或 RecyclerView 介面卡中使用資料繫結 ( DataBinding )
DataBindingUtil 或 生成的佈局繫結類deinflate() 方法,如以下程式碼示例所示:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); // or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Data Binding繫結表示式:
xml裡支援使用以下表達式:
算術運算子 + - / * %字串連線運算子 +邏輯運算子 && ||二元運算子 & | ^一元運算子 + - ! ~移位運算子 >> >>> <<比較運算子 == > < >= <=(請注意,< 需要轉義為 <)instanceof分組運算子 ()字面量運算子 - 字元、字串、數字、null型別轉換方法呼叫欄位訪問陣列訪問 []三元運算子 ?:不支援以下表達式:
thissupernew顯式泛型呼叫a、變數
1、生成的資料繫結程式碼會自動檢查有沒有 null 值並避免出現 Null 指標異常。
2、例如,在表示式 @{user.name} 中,如果 user 為 Null,則為 user.name 分配預設值 null。
3、如果您引用 user.age,其中 age 的型別為 int,則資料繫結使用預設值 0。
如果左邊運算數不是 null,則 Null 合併運算子 (??) 選擇左邊運算數,如果左邊運算數為 null,則選擇右邊運算數。
android:text="@{user.displayName ?? user.lastName}"//等效於如下三目表示式android:text="@{user.displayName != null ? user.displayName : user.lastName}"
c、檢視引用
1、表示式可以透過以下語法按 ID 引用佈局中的其他檢視:
2、繫結類將 ID 轉換為駝峰式大小寫。
3、在以下示例中,TextView 檢視引用同一佈局中的 EditText 檢視:android:text="@{exampleText.text}"
<EditText android:id="@+id/example_text" android:layout_height="wrap_content" android:layout_width="match_parent"/> <TextView android:id="@+id/example_output" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{exampleText.text}"/>
d、顯示隱藏控制
1、首先在 xml 的 data 節點中引用View
2、然後設定visibility
<data> <import type="android.view.View"/></data>android:visibility="@{student.boy ? View.VISIBLE : View.INVISIBLE}"
e、事件處理
方法引用:
==android:onClick="@{handlers::onClickFriend}"==
public class MyHandlers { public void onClickFriend(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> <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>
注意:
1、在表示式中,您可以引用符合監聽器方法簽名的方法。
2、當表示式求值結果為方法引用時,資料繫結會將方法引用和所有者物件封裝到監聽器中,並在目標檢視上設定該監聽器。
3、如果表示式的求值結果為 null,則資料繫結不會建立監聽器,而是設定 null 監聽器。
4、表示式中的方法簽名必須與監聽器物件中的方法簽名完全一致。
監聽器繫結:
==android:onClick="@{() -> presenter.onSaveClick(task)}"==
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>
以上,我們尚未定義傳遞給 onClick(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)}"
監聽長按事件,表示式應返回一個布林值。
public class Presenter { public boolean onLongClick(View view, Task task) { } } android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
注意:
1、監聽器繫結這些是在事件發生時進行求值的 lambda 表示式。
2、資料繫結始終會建立一個要在檢視上設定的監聽器。
3、事件被分派後,監聽器會對 lambda 表示式進行求值。
dataBinding 可以拓展 View 屬性
以前想要給 ImageView 增加幾個屬性,必須要寫個自定義的 ImageView 在建構函式中一頓解析。
那看看使用 dataBinding 如何拓展 View 屬性。
public class CustomImageView extends ImageView{ //需要使用BindingAdapter註解並標記在public static方法上。 //value中的欄位隨意新增和方法引數一一對應即可。 @BindingAdapter(value = {"image_url", "isCircle"}) public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle) { view.setImageUrl(view, imageUrl, isCircle, 0); } //requireAll = false代表是否以下三個屬性在xml中同時使用才會呼叫到該方法 //為false的話,只要有一個屬性被使用就能呼叫到該方法 @BindingAdapter(value = {"image_url", "isCircle", "radius"}, requireAll = false) public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle, int radius) { ...... } }//在佈局檔案中如下使用,便能實現圖片圓角和資源Url繫結的功能 <CustomImageView ....... app:image_url ="@{user.avatar}" app:radius="@{50}"></CustomImageView>
BindingAdapter
繫結介面卡,是 Jetpack DataBinding 中用來擴展布局 xml 屬性行為的註解;
允許你針對佈局 xml 中的一個或多個屬性進行繫結行為擴充套件;這個屬性可以是自定義屬性,也可以是原生屬性。這個擴充套件行為可以是簡單的ViewModel屬性與控制元件賦值繫結,也可以是關聯某個控制元件屬性的額外操作,例如在設定屬性之前進行值域檢查,或型別轉換,或者統一處理一些事情。後臺私信回覆 1024 免費領取 SpringCloud、SpringBoot,微信小程式、Java面試、資料結構、演算法等全套影片資料。