前言
Jetpack 是一个丰富的组件库,它的组件库按类别分为 4 类,分别是架构(Architecture)、界面(UI)、 行为(behavior)和基础(foundation)。
每个组件都可以单独使用,也可以配合在一起使-用。每个组件都给用户提供了一个标准, 能够帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者能够集中精力编写重要的业务代码。
DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。使其维护起来更加方便,架构更明确简介。
使用前需要了解
运行环境: Android Studio Chipmunk | 2021.2.1 Patch 1 Build #AI-212.5712.43.2112.8609683, built on May 19, 2022 Runtime version: 11.0.12+7-b1504.28-7817840 amd64
- 应用DataBinding,在App的build.gradle:
android {
buildFeatures {
dataBinding = true
}
- 想在哪个页面使用DataBinding,就修改对应的布局文件
添加 layout 标签
<?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">
<!--我的布局文件-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ActivityMainBinding mBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
注意:DataBinding实现了ViewBinding ,所以ViewBinding的用法也适用。
一、基础用法
当前事例实现:
- 向XML中设置指定的Data格式数据,提供给View引用
- 向XML设置点击事件
- 向XML设置方法所在类,调用静态方法
- 引用string资源文件
XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!--提供方法的类-->
<import type="com.yoshin.kt.demo_databinding.StringUtils" />
<!--点击事件-->
<import type="com.yoshin.kt.demo_databinding.MainActivity.Listener" />
<!--实体类-->
<variable
name="student"
type="com.yoshin.kt.demo_databinding.bean.Student" />
<!--实体类-->
<variable
name="student2"
type="com.yoshin.kt.demo_databinding.bean.Student" />
<!--点击事件-->
<variable
name="lintener"
type="Listener" />
</data>
<!--我的布局文件-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="你好 世界!" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:onClick="@{()->lintener.onClick(student)}"
android:text="@{@string/student_name + student.name,default = 小明}"
android:textColor="@color/teal_200" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="@{StringUtils.getAge(student.year)}"
android:textColor="@color/teal_200" />
<EditText
android:layout_width="match_parent"
android:layout_height="50dp"
android:afterTextChanged="@{lintener.afterStudentNameChanged}"
android:gravity="center"
android:text="@{@string/student_2 + @string/student_name + student2.name,default = 小明}"
android:textColor="@color/teal_200" />
</LinearLayout>
</layout>
Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding mBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
Student student = new Student();
student.setName("小黑");
student.setYear("18");
Student student2 = new Student();
student2.setName("小绿");
mBinding.setStudent(student);
mBinding.setStudent2(student2);
mBinding.setLintener(new Listener());
}
public class Listener {
public void onClick(Student student) {
Toast.makeText(MainActivity.this, student.getName(), Toast.LENGTH_SHORT).show();
}
public void afterStudentNameChanged(Editable editable) {
Toast.makeText(MainActivity.this, editable.toString(), Toast.LENGTH_SHORT).show();
}
}
此时的写法,数据没有绑定
二、引入布局(include、viewStub)
以 include 为例(DataBinding不支持merge标签):
view_include_patriarch.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="patriarch"
type="com.yoshin.kt.demo_databinding.bean.Patriarch" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="@{@string/str_name + patriarch.name,default = 小明的爸爸}"
android:textColor="@color/teal_200" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:binding="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<!--提供方法的类-->
<import type="com.yoshin.kt.demo_databinding.StringUtils" />
<!--点击事件-->
<import type="com.yoshin.kt.demo_databinding.MainActivity.Listener" />
<!--实体类-->
<variable
name="student"
type="com.yoshin.kt.demo_databinding.bean.Student" />
<!--实体类-->
<variable
name="student2"
type="com.yoshin.kt.demo_databinding.bean.Student" />
<!--点击事件-->
<variable
name="lintener"
type="Listener" />
</data>
<!--我的布局文件-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
... 略
<include
android:id="@+id/ic_patriarch"
layout="@layout/view_include_patriarch"
binding:patriarch="@{student.patriarch}" />
</LinearLayout>
</layout>
需要在main.xml里添加:
xmlns:binding="http://schemas.android.com/apk/res-auto"
但是鼓捣了半天都引用不到属性
不过没有关系!!!硬写吧,是好用的~ 这应该是个BUG吧
参考:dataBinding中使用include :https://blog.csdn.net/chuyouyinghe/article/details/124354463
三、单向绑定 (Data变化通知View)
如下事例延续上面的操作续写
1、BaseObservable
public class Student extends BaseObservable {
@Bindable
private String name;
@Bindable
private String year;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
notifyPropertyChanged(BR.year);
}
}
如上,如果通过 setYear(String year) 方法设置 year 时,如果数据有变化就会通知View进行更新,通过重写 TextView.setText() 方法确认了这一点,如果数据无变换不会通知到View更新; setName(String name) 调用不会通知View更新,因为没有调用到 notifyPropertyChanged(id) 。
2、ObservableField
参考 :Android 安卓DataBinding(四)·单向绑定 ObservableField:https://blog.csdn.net/qq_40881680/article/details/101847386
续写测试ObservableField
参考上面做的,我这里也不好使啊,记一下!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!不好使!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public class Student extends BaseObservable {
private final ObservableField<String> name = new ObservableField<>();
private final ObservableField<String> year = new ObservableField<>();
private final ObservableField<Patriarch> patriarch = new ObservableField<>();
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public String getYear() {
return year.get();
}
public void setYear(String year) {
this.year.set(year);
}
public Patriarch getPatriarch() {
return patriarch.get();
}
public void setPatriarch(Patriarch patriarch) {
this.patriarch.set(patriarch);
}
}
在调用如下代码时,XML没有跟着实时刷新啊,(⊙o⊙)…不理解源码的痛
student.setYear(String.valueOf(System.currentTimeMillis()));
public class Patriarch {
private ObservableField<String> name;
private ObservableInt intId;
private ObservableLong longId;
private ObservableDouble doubleId;
private ObservableBoolean isOk;
private ObservableByte oByte;
private ObservableFloat floatId;
private ObservableChar charId;
private ObservableShort shortId;
public Patriarch() {
name = new ObservableField<>();
}
private ObservableParcelable<Friend> friend;
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
ObservableList<String> list;
ObservableArrayList<String> arrayList;
ObservableMap<String, String> map;
ObservableArrayMap<String, String> arrayMap;
}
先这样吧 。没好使。
四、双向绑定 (Data变化通知View,View变化反馈给数据)
<TextView
android:id="@+id/tv_2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:onClick="@{()->lintener.onClick(student)}"
android:text="@={student.name,default = 小明}"
android:textColor="@color/teal_200" />
如上,即双向绑定的写法
@={student.name,default = 小明}
区别于正常写法,需要多写个= 号
注意是,如果写了“=”就不能直接引用string文件了,也不能引用静态方法。
测试的时候,配合的是 标题三、单向绑定,中的 1.BaseObservable进行的测试,验证双向绑定OK
五、配合LiveData的写法
1、事例延续上面代码修改,做双向绑定
public class MainViewModel extends AndroidViewModel {
public MainViewModel(@NonNull Application application) {
super(application);
}
final MutableLiveData<Student> mutableLiveData = new MutableLiveData<>();
public MutableLiveData<Student> getMutableLiveData() {
return mutableLiveData;
}
public void setMutableLiveData(Student student) {
mutableLiveData.setValue(student);
}
final MutableLiveData<String> stringMutableLiveData = new MutableLiveData<>();
public MutableLiveData<String> getStringMutableLiveData() {
return stringMutableLiveData;
}
public void setStringMutableLiveData(String str) {
stringMutableLiveData.setValue(str);
}
<data>
.... 略
<variable
name="viewModel"
type="com.yoshin.kt.demo_databinding.MainViewModel" />
<!--我的布局文件-->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
... 略
<EditText
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:onClick="@{()->lintener.onClick(student)}"
android:text="@={viewModel.mutableLiveData.name}"
android:textColor="@color/teal_200" />
<com.yoshin.kt.demo_databinding.MyTextView
android:id="@+id/tv_2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="@={viewModel.stringMutableLiveData}"
android:textColor="@color/teal_200" />
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
ActivityMainBinding mBinding;
private MainViewModel mVM
= ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication()).create(MainViewModel.class);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
... 略
mBinding.setLifecycleOwner(this);
mBinding.setViewModel(mVM);
mVM.getMutableLiveData().observe(this, new Observer<Student>() {
@Override
public void onChanged(Student student) {
Log.i(TAG, " getMutableLiveData observe == " + new Gson().toJson(student));
}
});
mVM.setMutableLiveData(student);
mVM.getStringMutableLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Log.i(TAG, " getStringMutableLiveData observe == " + new Gson().toJson(s));
}
});
mVM.setStringMutableLiveData("这就是个String");
如此,可以实现双向绑定。
虽然实现了双向绑定,但是还有需要说的地方: LiveData <T 的泛型 T 是JavaBean 还是String 类型,影响观察者模式的回调
- 当
T 是 Student (JavaBean)
调用 mVM.setMutableLiveData(student); ,日志 getMutableLiveData observe == xxx 只会输出一次,无论值本身地址变化,还是内部数据变化; 调用 mBinding.tv1.setText(“xxxx”); ,日志 getMutableLiveData observe == xxx 不会输出,但是值本身是变化的(通过点击按钮打印,mutableLiveData 内部值是变化的)。
- 当
T 是 String
进入页面第一次调用 mVM.setStringMutableLiveData(str); ,日志 getStringMutableLiveData observe == xxx 会输出一次; 之后,调用 mVM.setStringMutableLiveData(str); ,如果值是有变化的,日志 getStringMutableLiveData observe == xxx 会输出两次,重写了TextView,发现setText()的方法,夹在中间; 如下:
14:10:29.827 14131-14131/com.yoshin.kt.demo_databinding I/MainActivity: getStringMutableLiveData observe == "setString 修改后String的值"
14:10:29.829 14131-14131/com.yoshin.kt.demo_databinding I/Main-MyTextView: setText
14:10:29.832 14131-14131/com.yoshin.kt.demo_databinding I/MainActivity: getStringMutableLiveData observe == "setString 修改后String的值"
如果值没有变化,日志 getStringMutableLiveData observe == xxx 会输出一次;
调用 mBinding.tv2.setText(“xxxx”); ,日志 getStringMutableLiveData observe == xxx 会输出一次。
综上所述,推荐使用当 T 是 (JavaBean) 的方式,容易控制。
2、引入布局场景
二、引入布局(include、viewStub)的写法在这就失效了,这里就要修改了,如下即可:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.yoshin.kt.demo_databinding.MainViewModel" />
<variable
name="patriarch"
type="com.yoshin.kt.demo_databinding.bean.Patriarch" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:text="@{@string/str_name + viewModel.mutableLiveData.patriarch.name,default = 小明的爸爸}"
android:textColor="@color/teal_200" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
改成 传入 viewModel,就可以在数据改变的时候View更新。
六、BindingAdapter
用途: 支持自定义属性,或者是修改原有属性
一个参数的
public class ImageBindingAdapter {
@BindingAdapter("imageUrl")
public static void setImageUrl(ImageView view, String url) {
Log.i(TAG, "setImageUrl: ");
Glide.with(view).load(url).into(view);
}
<ImageView
imageUrl="@{viewModel.mutableLiveData.url}"
android:layout_width="100dp"
android:layout_height="100dp"/>
多个参数的
@BindingAdapter(value = {"imageUrl", "placeholder", "error"},requireAll = false)
public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {
RequestOptions options = new RequestOptions();
options.placeholder(placeholder);
options.error(error);
Glide.with(view).load(url).apply(options).into(view);
}
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"
app:imageUrl="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"
app:placeholder="@{@drawable/icon}"/>
public class TextViewAdapter {
@BindingAdapter("android:text")
public static void setText(TextView view, CharSequence text) {
String txt = text.toString().toLowerCase();
view.setText(txt);
}
}
<!--使用dataBinding的TextView-->
<TextView
android:id="@+id/tvData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#a37c7c"
android:text="@{`这是TextView...`}"
android:textSize="16sp" />
这么写完,databinding都会调用这个方法,不推荐
转 Android Jetpack组件之BindingAdapter详解:https://blog.csdn.net/jzman/article/details/106699816
七、BindingConversion
Android Jetpack组件之BindingAdapter详解:https://blog.csdn.net/jzman/article/details/106699816
Android DataBinding 从入门到进阶,看这一篇就够:https://blog.csdn.net/Eqiqi/article/details/121670801
八、支持的运算符和关键字
绑定表达式支持的运算符和关键字
算术运算符 + - / * % 字符串连接运算符 + 逻辑运算符 && || 二元运算符 & | ^ 一元运算符 + - ! ~ 移位运算符 >> >>> << 比较运算符 == > < >= <=(请注意,< 需要转义为 <) instanceof 分组运算符 () 字面量运算符 - 字符、字符串、数字、null 类型转换 方法调用 字段访问 数组访问 [] 三元运算符 ?: Null 合并运算符 ?? 属性引用. 集合操作[],可用于数组,列表,映射; 字面常量 资源@,但是有些资源类型需要显式指明;
Android-DataBinding-使用-高阶:https://www.jianshu.com/p/b5762e72f56f
参考地址
Android DataBinding 从入门到进阶,看这一篇就够:https://blog.csdn.net/Eqiqi/article/details/121670801
https://wenku.baidu.com/view/0a12a6356f175f0e7cd184254b35eefdc8d31592.html
https://developer.android.google.cn/jetpack/androidx/releases/databinding
转自 Android Jetpack架构全家桶,学完可从零搭建一个Android项目架构:http://px.sxjnk.cn/enjoy/advertorial/article_6
|