侧边栏壁纸
  • 累计撰写 416 篇文章
  • 累计创建 65 个标签
  • 累计收到 150 条评论

目 录CONTENT

文章目录

Android Jetpack 之 Databinding 组件

Z同学
2021-08-09 / 0 评论 / 2 点赞 / 1,138 阅读 / 11,252 字
温馨提示:
本文最后更新于 2021-11-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

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 标签作为根节点。然后在根节点中包括dataview标签

其中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 标签下,只有两个标签,分别是variableimport

先介绍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>

其中 “<>” 符号需要进行转义

<> 的转义写法

< 使用字符:&lt;

> 使用字符:&gt;

语法

我们在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 标签定义的数据,将会自动生成setget方法

自定义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 提供了get 和set 方法

除此之外,还有封装好了可以直接使用的类: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 进行赋值。

2

评论区