Android Jetpack - DataBinding

dean_mh發表於2020-10-21

簡介

使用宣告性格式(而非程式化地)將佈局中的介面元件繫結到應用中的資料來源,即在xml裡面就將view和資料繫結,然後當資料發生變化時view能自動變。
ViewBinding和DataBinding相輔相成的,它們都會生成額外的一個binding類,如果頁面需要資料就使用DataBinding,如果頁面簡單使用ViewBinding更快,不管用哪個,findViewById可以告別了

簡單使用:(不像ViewBinding那樣對AS版本有要求,Android Plugin for Gradle 版本在1.5.0以上即可)

  1. 在需要用的module的build.gradle中新增
android {
	...
	dataBinding {
		enabled = true
	}
}
  1. 寫一個bean類(看需求,這邊主要是說明頁面需要的資料)
@Entity
public class User {
	@PrimaryKey(autoGenerate = true)
	public int uid;
	@ColumnInfo(name = "first_name", defaultValue = "a")
	public String firstName;
	@ColumnInfo(name = "last_name")
	public String lastName;
}
  1. 在xml中
<!--如果使用databinding根節點都變成了layout-->
<layout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	xmlns:tools="http://schemas.android.com/tools">
	<!--這邊宣告該頁面需要使用的資料-->
	<data>
		<variable
			<!--變數名-->
			name="user"
			<!--變數型別-->
			type="com.dean.mh_calendar.model.User" />
	</data>
	
	<!--這邊和之前一樣佈局-->
	<androidx.constraintlayout.widget.ConstraintLayout
		android:layout_width="match_parent"
		android:layout_height="match_parent">

		<TextView
			android:id="@+id/tv_first_name"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			<!--在需要的地方直接使用資料-->
			android:text="@{user.firstName}"
			app:layout_constraintLeft_toLeftOf="parent"
			app:layout_constraintRight_toRightOf="parent"
			app:layout_constraintTop_toTopOf="parent"
			app:layout_constraintBottom_toBottomOf="parent"/>
		<TextView
			android:id="@+id/tv_last_name"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="@{user.lastName}"
			app:layout_constraintLeft_toLeftOf="parent"
			app:layout_constraintRight_toRightOf="parent"
			app:layout_constraintTop_toBottomOf="@+id/tv_first_name"/>

	</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

繫結xml中使用的資料有自動檢測空指標功能,如果為null那麼賦予的值就是null,如果是int等字元型,預設值是0

在繫結的xml中使用資料和直接在java中使用基本一樣,只是不可以使用new、this、super 和 顯式泛型呼叫
比如可以:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

除此以外,還可以使用一些新的運算子

  • ??:Null 合併運算子
android:text="@{user.firstName ?? user.lastName}"
當user.firstName為null時使用user.lastName,不為null使用user.firstName
  • 檢視引用:按 ID 引用佈局中的其他檢視
<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"
	<!--在此處引用了上面EditText的Text值,引用名稱按照駝峰命名法引用-->
	android:text="@{exampleText.text}"/>
  • 使用集合
    集合使用都用[]來取值,但是在data中引入的時候需要注意
<data>
	<import type="java.util.List"/>
	<!-- < 必須 轉義成 &lt;-->
	<variable name="list" type="List&lt;String>"/>
	<variable name="index" type="int"/>
</data>
android:text="@{list[index]}"
  • 字串字面量
    如果表示式裡面要用“”或者’’,外層就可以與之相反,即
android:text='@{map["firstName"]}'  
android:text="@{map[`firstName`]}"
  • 資源引用
    需要使用資源時可以直接使用
android:padding="@{large ? @dimen/largePadding : @dimen/smallPadding}"

如果是需要引數可以直接傳,比如格式字串

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

當一個複數帶有多個引數時,您必須傳遞所有引數

Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
  • 事件處理
    根據需要在xml中有兩種事件繫結處理方式:傳統的方法引用和監聽器繫結

它們的區別是:方法引用無法自定義入參,並且它是直接繫結方法,無論事件有沒有發生;而監聽器繫結可以自定義入參(兩種方式的是無法修改返回的),並且只有當事件發生時才繫結方法,還可以處理一些邏輯程式碼,但是谷歌不建議有業務相關的或者大量邏輯的
監聽器繫結:

  1. 寫事件處理方法,在定義方法時可以多一些引數
public class MainActivity extends AppCompatActivity{
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
		mBinding.setHanldeEvent(new HandleEvent());
	}

	public class HandlerEvent {
		public boolean backBtnOnLongClick(View view){
			Toast.makeText(view.getContext(),"backBtnOnLongClick",Toast.LENGTH_SHORT).show();
			return true;
		}
		public void backBtnOnClick(View view, String text){
			Toast.makeText(view.getContext(),"backBtnOnClick",Toast.LENGTH_SHORT).show();
		}
	}
}
  1. 在檢視中加入
<layout xmlns:android="http://schemas.android.com/apk/res/android">
	<data>
		<variable name="hanldeEvent" type="com.dean.smartapp.Main3Activity.HandleEvent" />
	</data>
	<LinearLayout
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:orientation="vertical">
		<Button
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="事件繫結"
			<!--使用時傳參,這邊的view2FunParams就是傳遞的text引數,必須使用&quot;轉義"號-->
			android:onClick="@{(view)->hanldeEvent.backBtnOnClick(view,&quot;view2FunParams&quot;)}"
			android:onLongClick="@{hanldeEvent::backBtnOnLongClick}"/>
	</LinearLayout>
</layout>
  • 類名衝突
    當匯入不同包下的類時會有類名衝突,這時可以使用alias來讓其中一個宣告別名
<data>
	<import type="java.util.List" alias="system_list"/>
</data>
  • 當databinding遇到包含include 和 merge
    當有子佈局時,繫結的資料可以直接傳遞
<!--父佈局-->
<?xml version="1.0" encoding="utf-8"?>
<layout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto"
	xmlns:tools="http://schemas.android.com/tools">
	<data>
		<variable name="user" type="com.dean.mh_calendar.model.User" />
	</data>
	<androidx.constraintlayout.widget.ConstraintLayout
		android:layout_width="match_parent"
		android:layout_height="match_parent">
		<include android:id="@+id/include_layout"
			layout="@layout/test_include"
			<!--將值傳遞下去-->
			app:user="@{user}"/>

	</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<!--子佈局-->
<?xml version="1.0" encoding="utf-8"?>
<layout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	xmlns:app="http://schemas.android.com/apk/res-auto">
	<data>
		<variable name="user" type="com.dean.mh_calendar.model.User" />
	</data>
	<!--此處merge會報錯,但是編譯執行沒有問題-->
	<merge
		tools:parentTag="android.support.constraint.ConstraintLayout">
		<TextView
			android:id="@+id/tv_include_name"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="@{user.firstName + user.lastName}"
			app:layout_constraintLeft_toLeftOf="parent"
			app:layout_constraintRight_toRightOf="parent"
			app:layout_constraintTop_toTopOf="parent"
			app:layout_constraintBottom_toBottomOf="parent"/>
	</merge>
</layout>

如果子佈局裡面還有子佈局,則不能直接在merge下寫include

<layout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:app="http://schemas.android.com/apk/res-auto">
	<data>
		<variable
			name="user"
			type="com.dean.mh_calendar.model.User" />
	</data>
	<merge>
		<include android:id="@+id/include_layout"
			layout="@layout/test_include2"
			app:user="@{user}"/>
	</merge>
</layout>

這樣寫在編譯時會報錯Data binding does not support include elements as direct children of a merge element.這時可以在include外套一層佈局

  1. 在介面控制器中繫結資料
//一旦在xml中使用layout作為根佈局,會自動生成相應的繫結類
private ActivityMain3Binding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	//通過DataBindingUtil拿到生成的繫結類
	mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main3);
	mBinding.setUser(users.get(0));
}

在activity中使用的是DataBindingUtil.setContentView(this, R.layout.activity_main3);獲取繫結類
在fragment或者recycleView的adapter中使用DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);獲取繫結類

  1. 使用可觀察的資料物件
    上面這些只是在螢幕上展示資料,還可以讓可觀察物件繫結到介面,這時一旦該資料物件的屬性發生更改,介面會自動更新。

相關文章