Jetpack学习-7-Databinding的基本使用及单向&双向绑定

DataBinding是Jetpack中比较受非议的一个库,很多开发者不习惯且不喜欢在xml布局中进行java层代码的数据绑定,如果是简单的页面,用不上databinding,而复杂的页面,用了databinding对后续的问题排查又是个蛋疼的事,但不妨碍学习databinding的使用。

优点:

1,UI控件的代码在布局中编写,降低页面与布局文件的耦合度

2,不需要写findviewById(viewbinding表示它也可以)

3,布局文件可以包含简单的业务逻辑(个人不建议)

使用基本操作

1,在app的build.gradle文件中开启数据绑定功能

android {
    ...
    dataBinding{
        enabled = true
    }
}

2,修改布局文件

将光标移到根目录,会出现小灯泡,点击,选择 Convert to data binding layout(转变为databinding布局)

 自动生成如下内容

activity_main6.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>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World,I am databinding !" />

    </LinearLayout>
</layout>

也可以手动添加<layout> <data>等标签转变布局

rebuild后会自动生成对应的binding类

3,使用绑定类

databinding    

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMain6Binding activityMain6Binding = DataBindingUtil.setContentView(this, R.layout.activity_main6);
        activityMain6Binding.tvMessage.setText("hello,databinding");
    }

另比较viewbinding的使用

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMain5Binding binding = ActivityMain5Binding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        binding.tvMessage.setText("hello,viewbinding");
        
    }

可以看出,viewbinding就是databinding的子类,底层也是databinding来实现的,数据绑定离不开识图的绑定

4,布局中设置变量并进行控件绑定
<?xml version="1.0" encoding="utf-8"?>
<layout                     //表示这是databinding的布局文件 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>//用于放置布局中控件所需要的变量

        <variable
            name="student"
            type="com.sun.databinding.Student" />

        <variable
            name="name"
            type="String" />

        <variable
            name="age"
            type="int" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World,I am databinding !" />

        <TextView
            android:id="@+id/tv_student_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{student.toString()}" />

        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{name}" />

        <TextView
            android:id="@+id/tv_age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(age)}" />

    </LinearLayout>
</layout>

5,将数据引入到布局中

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMain6Binding activityMain6Binding = DataBindingUtil.setContentView(this, R.layout.activity_main6);
        activityMain6Binding.tvMessage.setText("hello,databinding");

        //第一种方式(个人推荐这种方式,这种方式可以强制设置属性的类型,避免很多传参问题,同时也不会把其他布局的变量进行设置)
        activityMain6Binding.setAge(12);//传入整型时会默认为资源id,要进行二次处理
        activityMain6Binding.setName("sunguanyong");
        activityMain6Binding.setStudent(new Student("hanmeimei", 14));

        //第二种方式(BR类似于项目的R文件,管理所有布局变量的id,这里必须传入正确的id才能传好数据,而且无法跳转到布局中的变量位置,所以不推荐)
        activityMain6Binding.setVariable(BR.age, 12);
        activityMain6Binding.setVariable(BR.name, "sunguanyong");
        //activityMain6Binding.setVariable(BR.name1, "sunguanyong");//这个name1是另一个布局中的变量,也能进行设置操作,这就很容易引起错误,实际不会崩溃,但没有作用
        activityMain6Binding.setVariable(BR.student, new Student("hanmeimei", 15));
    }

这样,最基本的databinding的使用已经完成,下面拓展别的用法。

6,布局中绑定静态方法

1,静态方法 
package com.sun.databinding;

public class StaticMethod {
    public static String getName() {
        return "sunguanyong 2022";
    }

    public static String getName(int age) {
        return "hello ,lilei ," + age;
    }
}


2,布局中导入并引用
<data>
        ...
        <import type="com.sun.databinding.StaticMethod" />
</data>

3,调用静态方法
 <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{StaticMethod.getName()}" />

<TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{StaticMethod.getName(age)}" />

7,绑定点击事件

1,在页面中设置点击逻辑
 /**
     * 点击事件
     */
    public class EventClick {

        private Context context;

        public EventClick(Context context) {
            this.context = context;
        }

        public void clickToast(View view) {
            Log.e(TAG, "view的id=" + view.getId() + "   " + ((Button) view).getText());//id=-1,因为我没有设置id
            Toast.makeText(context, "我通过 EventClick databind了", Toast.LENGTH_SHORT).show();
        }
    }


2,在布局中使用添加EventClick的实例

    <data>
        ...
        <variable
            name="eventClick"
            type="com.sun.databinding.MainActivity.EventClick" />
    </data>

3,绑定到button的点击事件中
  <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:onClick="@{eventClick.clickToast}"   //或者   android:onClick="@{eventClick::clickToast}" 
      android:text="按钮点击"/>

8,二级页面传值,所谓的二级页面其实是对布局中layout包裹的布局进行传值

1,二级页面简单编写
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="studentinfo"
            type="com.sun.databinding.Student" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="我是layout布局" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{studentinfo.toString()}" />
    </LinearLayout>
</layout>


2,添加二级布局,同时根据二级页面的变量进行传值
<include
       layout="@layout/layout_common"
      app:studentinfo="@{student}" />//给studentinfo的变量传入值,值为页面传来的student

上面的数据绑定操作都是单向操作,页面更改数据从而导致布局显示变化,也可以实现双向绑定。

9,要想实现双向绑定,肯定离不开观察者模式,双向绑定也是这一模式的具体体现

方法1(使用BaseObservable)

1,建立 被观察类

public class StudentObservable extends BaseObservable {

    public String name;

    public StudentObservable(String name) {
        this.name = name;
    }

    @Bindable    //告诉编译器,对这个字段进行双向绑定
    public String getName(){
        return name;
    }

    public void setName(String name){//数据发生变化时自动调用,也可以主动调用
        if(!TextUtils.isEmpty(name)&&!this.name.equals(name)){
            this.name=name;
            notifyPropertyChanged(BR.name);//使用BaseObservable中的notifyPropertyChanged(BR.id)进行通知
        }
    }
}


2,布局显示
<data>
        ...
        <variable
            name="student_observable"
            type="com.sun.databinding.StudentObservable" />

</data>

 <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="输入框1"
            android:text="@={student_observable.name}" />//此处用@=,可接收属性的数据更改并同时监听用户更新

<EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="输入框2"
            android:text="@={student_observable.name}" />//此处用@=

使用这种方法必须继承自BaseObservable成为被观察者,另外,在Getter方法添加@Bindable标签,在setting中进行notifyPropertyChanged(),也可以通过更简单的ObservableField<T>的方式实现双向绑定。

方法2(使用ObservableField<T>)

1,创建类
package com.sun.databinding;

import androidx.databinding.ObservableField;

public class StudentObservableField {

    //ObservableField可以看成是对BaseObservable,set,get, @Bindable,notify进行了封装
    //基本数据类型:ObservableInt,ObservableFloat等
    public ObservableField<String> name;

    public StudentObservableField(String name) {
        this.name = new ObservableField<String>(name);
    }

}

2,布局及使用同BaseObservable是一样的
<data>
        ...
        <variable
            name="studentfield"
            type="com.sun.databinding.StudentObservableField" />

    </data>



        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="输入框1"
            android:text="@={studentfield.name}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="输入框2"
            android:text="@={studentfield.name}" />

BaseObservable与ObservableField的比较:

看源码知道ObservableField是对BaseObservable进行了封装,站远一点分析,属性依附在对象中,BaseObservable作用在对象上,所以需要继承才能让内部的属性可以双向数据绑定,而ObservableField则作用在属性上,直接做了封装,所以类就不用做特殊处理显得更加简单。

至此,databinding的初步使用已经完成,如果再看官网的整个流程的话,还是有很多东西没有覆盖到的。虽然很多时候databinding在开发过程中饱受争议,但不妨碍我们学习使用它,内部的实现很多地方还是值得学习的。

这一篇只介绍使用,下一篇开始将深入接触bindingAdapter的使用和对databinding的源码分析。

Android-Jetpack代码位置:github

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr_Sun_01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值