前言
Databinding 是一个数据绑定库。是实现数据和UI绑定的框架。
也是现在主流的开发使用的一个工具。
但是注意,这所说的数据和UI绑定和我们所说的ViewModel是为了解决数据和UI的绑定。
不冲突。
因为ViewModel绑定的是业务数据和Activity 界面之间的交互。
而Databinding 是绑定View 和xml 之间的数据交互。
库依赖
如果要使用Databinding 库,或者你依赖的aar库之中使用了Databinding 。
那么你都需要在build.gradle文件之中 开启Databinding支持。
否则将会报错。
在module 的 build.gradle 之中开启即可。
android {
....
dataBinding {
enabled = true
}
}
PS: 如果你的Android Studio 版本太低,会无法依赖。
有别于其他的库,databinding库的导入不用我们主动填写implementation
。指向maven库。
AS 会自动导入并依赖相关的库。
如果 dataBinding方法过时可以使用下面的方法。 因为下面的方法兼容了kotlin。 是最新的写法。
android {
....
buildFeatures {
dataBinding = true
}
}
使用
布局
DataBinding 的布局文件,使用layout
标签作为根节点。然后在根节点中包括data
和view
标签
其中view
标签就是我们通常情况下的xml布局。
示例:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
</layout>
这是一个很简单的布局结构,LinearLayout我们都了解。我们主要需要了解<Data>
标签的定义。
而在Data
标签下,只有两个标签,分别是variable
和 import
先介绍variable
标签
例如: 我们这个就是创建了一个变量。变量名是user,type 指向了包名。
<data>
<variable
name="user"
type="com.zinyan.bean.User" />
</data>
如果是基础数据类型 可以直接写
示例:
<data>
<variable
name="user"
type="com.zinyan.demo.User" />
<variable
name="name"
type="String" />
<variable
name="six"
type="int" />
<variable
name="isShowControl"
type="boolean" />
</data>
import 就是给我们导入其他类使用的。
例如我们需要使用一些全局常亮值,例如View.GONE 。等,我们不可能直接通过variable 创建一个View 对象吧。
那么我们就可以通过import 添加一个导入就可以使用了。
例如:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="isShowControl"
type="boolean" />
<import type="android.view.View" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{isShowControl?View.VISIBLE:View.INVISIBLE}"
android:text="TextView" />
</LinearLayout>
例如 这个例子,就是通过isShowControl
决定TextView
是否显示。
如果有两个导入的类出现了名称重复,造成的冲突。我们可以设置通过别名设置
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
我们可以通过import 的引入减少variable 中type 的长度
示例:
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
其中 “<>” 符号需要进行转义
<>
的转义写法
<
使用字符:<
>
使用字符:>
语法
我们在Data 之中定义的数据和变量,都可以通过 @{}
语法 在下面的xml 布局文件中直接引用。
数据绑定
我们按照上面写法创建完xml之后。AS会自动帮我们生成一个java类(ps:如果是kotlin就是kt文件)。
例如我们创建的xml 文件名为: activity_tow.xml 那么AS会自动生成一个 ActivityTwoBinding 的文件。
会根据我们的xml文件名去除下划线并按照驼峰命名并添加Binding做后缀。
我们之后所有与xml文件打交道都是通过该Binding文件。
示例: 将binding文件绑定到指定的Activity之中
public class TwoActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityTwoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_two);
binding.textView2.setText("修改");
binding.setIsShowControl(false);
binding.setName("名称");
binding.setSix(12);
}
}
我们可以直接使用binding 对象获取到View 对象。
而我们通过variable
标签定义的数据,将会自动生成set
和get
方法
自定义Binding类存放路径
我们生成的binding 在你编译app 之后,会在build/generated/data_binding_base_class_source_out
文件夹下生成。
例如我们上面的例子的ActivityTwoBinding.java
而它的默认包名 : 就是我们的app定义的包名+databinding 文件夹下面。
如果我们不想使用默认路径,我们可以通过自定义路径来实现
实例:
<!-- 自定义Binding类名 -->
<data class="Zinyan">
...
</data>
<!-- 自定义Binding存放路径,.代表module根目录 在modlle 包名称后面 -->
<data class=".Zinyan">
...
</data>
<!-- 自定义Binding存放路径,指定路径 -->
<data class="com.example.Zinyan">
...
</data>
布局 include
如果布局之中大量使用了include.我们该如何将当前的databinding 数据传递到include 布局里面呢?
示例:
<?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">
<data>
<variable
name="user"
type="com.zinyan.demo.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:visibility="@{isShowControl?View.VISIBLE:View.INVISIBLE}" />
<include
android:id="@+id/ww"
layout="@layout/layout_tt"
app:user="@{user}" />
</LinearLayout>
</layout>
layout_tt.xml 文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.zinyan.demo.User" />
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@{user.name}" />
</LinearLayout>
</layout>
在Activity 类中使用该方法:
ActivityTwoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_two);
User user = new User();
user.setName("测试");
binding.setUser(user);
binding.ww.text1.setText("修改");
表达式
在databinding 之中支持多少表达式?
名称 | 表达式 |
---|---|
数学计算 | + - / * % |
字符串连接 | + |
逻辑 | && || |
二进制 | & |^ |
一元运算符 | + - ! ~ |
位移 | >> >>> << |
比较 | == > < >= <= |
组 | () |
文字 | 字符,字符串,数字, null |
类型转换 | 类型强制转换 |
函数调用 | 本地方法调用 |
存取 | 字符存取,数组存取 [] |
三元运算符 | ?: |
进阶学习
主要介绍,使用databinding 的一些小技巧。
1.非null 时候使用左侧参数,null 时使用右侧数据值
android:text="@{user.displayName ?? user.lastName}"
2.集合 通用的集合类(arrays, lists, sparse lists, maps)
<data>
<import type="java.util.Map"/>
<import type="java.util.List"/>
//List 集合数组
<variable name="list" type="List<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
...
android:text="@{list[index]}"
...
android:text="@{map[key]}"
资源文件引入
引入字符串:
//String.xml 文件中
<string name="full_name">%1$s %2$s</string>
...
android:text="@{@string/full_name(user.firstName, user.lastName)}"
引入图片对象:
android:src="@{isPlay?@drawable/cn_ic_pause:@drawable/cn_ic_play}"
3.动态更新
绑定完毕后,数据字段的修改,并不会自动更新到UI之中。需要我们主动调用修改方法。
针对刷新需求,Databinding 库提供了Observable监听机制。
用来监听实体类的属性的变化。发生变化后。自动更新xml 布局的显示效果。
方案1.实体类需要继承 BaseObservable
示例:
实体类代码:
public class User extends BaseObservable {
private String name;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//BR 是编译过程中生成的类,类似于R类。
notifyPropertyChanged(BR.name); //调用刷新
}
}
Activity 中的代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityTwoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_two);
User user = new User();
user.setName("测试");
binding.setUser(user);
binding.textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
binding.getUser().setName("Zinyan");
}
});
}
xml代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
>
<data>
<variable
name="user"
type="com.zinyan.demo.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}" />
</LinearLayout>
</layout>
最终 点击后就会显示zinyan 了。
上面仅适用于少量的数据变化。如果参数过多,每个get
方法都需要加上注解还在每个set
方法中调用notifyPropertyChanged
刷新。就会有点繁琐了。
方案2.实体属性值通过Observable 包裹
示例:
public class User {
private ObservableField<String> name = new ObservableField<>();
public User() {
}
public ObservableField<String> getName() {
return name;
}
}
ActivityTwoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_two);
User user = new User();
user.getName().set("测试");
binding.setUser(user);
binding.textView2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
binding.getUser().getName().set("zinyan");
}
});
ObservableField
除此之外,还有封装好了可以直接使用的类:ObservableBoolean
, ObservableByte
, ObservableChar
, ObservableShort
, ObservableInt
, ObservableLong
, ObservableFloat
, ObservableDouble
, ObservableParcelable
。
4.双向绑定
当UI改变的时候, 数据也发生变化。
表达式: @={user.firstName}
通常情况下用来改变指定字段的参数。
5. 自定义属性 BindingAdapter
DataBinding 之中支持全局自定义属性 主要依靠注解: @BindingAdapter
示例:
public class ViewBindingAdapter {
@BindingAdapter({"imageUrl"})
public static void loadImage(ImageView view, String url) {
Glide.with(view.getContext()).load(url).into(view);
}
@BindingAdapter("visibleGone")
public static void setVisibleGone(View view, boolean visible) {
view.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
定义之后,在整个res 目录下,任何layout 文件中都能使用该属性
xml 示例:
<?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">
<data>
<variable
name="url"
type="String" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
//当我们给url 传值的时候。 会自动调用ViewBindingAdapter的loadImage方法
app:imageUrl="@{url}" />
</RelativeLayout>
</layout>
ActivityCustomAttributeBinding binding = DataBindingUtil.setContentView(this,
R.layout.activity_custom_attribute);
binding.setUrl("https://zinyan.com/pic1.png");
6. 类型转换 -BindingConversion
数据类型转换的执行优先级高于BindingAdapter
可以针对所有指定的数据类型进行转换
@BindingConversion
public static String conversionString(String text) {
return text + "-conversionString";
}
//xml 布局内容
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{"xxx"}'
android:textAllCaps="false"/>
那么 TextView 的文本内容就会变成: xxx-conversionString
我们可以用它实现 字符串输入,转换成int 进行赋值。
评论区