Android Jetpack - DataBinding

it2024-03-13  68

简介

使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源,即在xml里面就将view和数据绑定,然后当数据发生变化时view能自动变。 ViewBinding和DataBinding相辅相成的,它们都会生成额外的一个binding类,如果页面需要数据就使用DataBinding,如果页面简单使用ViewBinding更快,不管用哪个,findViewById可以告别了

简单使用:(不像ViewBinding那样对AS版本有要求,Android Plugin for Gradle 版本在1.5.0以上即可)

在需要用的module的build.gradle中添加 android { ... dataBinding { enabled = true } } 写一个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; } 在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中有两种事件绑定处理方式:传统的方法引用和监听器绑定

它们的区别是:方法引用无法自定义入参,并且它是直接绑定方法,无论事件有没有发生;而监听器绑定可以自定义入参(两种方式的是无法修改返回的),并且只有当事件发生时才绑定方法,还可以处理一些逻辑代码,但是谷歌不建议有业务相关的或者大量逻辑的 监听器绑定:

写事件处理方法,在定义方法时可以多一些参数 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(); } } } 在视图中加入 <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外套一层布局

在界面控制器中绑定数据 //一旦在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);获取绑定类

使用可观察的数据对象 上面这些只是在屏幕上展示数据,还可以让可观察对象绑定到界面,这时一旦该数据对象的属性发生更改,界面会自动更新。
最新回复(0)