美文网首页
Databinding使用(java)

Databinding使用(java)

作者: Elek | 来源:发表于2019-12-09 13:58 被阅读0次

个人使用学习过程,感谢指出错误

集成

在要使用的module的build.gradle文件中添加如下就可以使用了

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

基础使用

使用之前要知道Databinding都能做什么? 简单的理解就是 省去早先findViewById()的相关操作 把操作都写在了xml文件中,那么xml文件就需要变得不一样

1. 改变xml的格式满足Databinding规则

image.png

在xml的根标签Alt + Enter就可以看到如下界面选择 Convert to data binding layout
会发现多了一个 data 标签 我们相关的数据就要放在data标签中
layout标签的直接子标签不能是merge,否则报错

2. 如何把数据引用到xml里

public class Bean1 {
    public Bean1(String name, int count, int total) {
        this.name = name;
        this.count = count;
        this.total = total;
    }

    private String name;
    public int count;
    public int total;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}

我们新建一个Bean引用数据毋庸置疑就是引用Bean1到data标签中

//第一种引入方式
    <data>
        <variable
            name="bean"
            type="com.xiaoyu.jetpack.dataBinding.bean.Bean1" />
    </data>
//第二种引入方式
     <data>
        <import type="com.xiaoyu.jetpack.dataBinding.bean.Bean1"
                alias="bean2"/> //设置别名
        <variable
            name="bean"
            type="Bean2" />
    </data>

如果引入相同名称的数据 import中可以设置别名

数据引用完成后就要把数据关联到对应的控件上了


image.png
  1. 只需要在设置文字时用@{ }然后在{}里获取数据就可以了如此简单
  2. 需要注意的是类型问题如果设置的数据不是String类型可以String.valueOf()方法转换一下
  3. 我们在开发的时候需要做界面调试文字颜色之类的没有界面不是很方便就可以给个默认值有两种方法直接写用字符串的引用个人推荐第二种

3. 代码绑定数据

上面所有的准备工作都已经做好了可是我们的bean中没有数据啊没有数据就没有显示啊
(不晓得为什么xml中设置的默认文字运行项目的时候也不显示)
接下来就说说数据是如何绑定的 (相信我特别难!!)

 Activity1Binding binding = DataBindingUtil.setContentView(this, R.layout.activity1);//上面的布局
 binding.setBean(new Bean1("apple", 1, 10));

上面代码的意思呢就是我要把xml和这个这个activity关联然后返回给我一个Activity1Binding实例
这个实例类是系统自动生成的命名规则就是你指定的xml文件名除去 _ 加上Binding后缀
第二行代码就是填充数据了执行代码就出现文字了

image.png

4. 在Fragment中绑定数据

public class FragmentBinding extends androidx.fragment.app.Fragment {

    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Activity1LayoutBinding inflate = DataBindingUtil.inflate(inflater, R.layout.activity1_layout, container, false);
        inflate.setBean(new Bean1("apple",2,20));
        return inflate.getRoot();
    }
}

数据与UI更新(单向绑定/双向绑定)

这块要说的是数据更改之后UI进行界面更新这里分为单向绑定双向绑定
单向绑定应用场景 : 数据更新后刷新UI
双向绑定应用场景 : Edittext文本输入文字同时更改TextView显示为输入的文字

1. 单向绑定之BaseObservable

先分析一波,数据更新后刷新UI,数据在哪里?当然是bean喽~绑定要在哪里做手脚?当然也是bean喽

创建bean继承BaseObservable

public class Bean2 extends BaseObservable {
    //如果是public在成员变量上注解
 //如果是 private的在get方法上添加注解
    @Bindable
    public String name;
   //如果是 private的在get方法上添加注解
    private int price;
    public int total;

    public Bean2(String name, int price, int total) {
        this.name = name;
        this.price = price;
        this.total = total;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(com.xiaoyu.jetpack.BR.name);
    }
 
    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
        notifyChange();
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}

创建layout

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="bean"
            type="com.xiaoyu.jetpack.dataBinding.bean.Bean2" />
    </data>

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

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{bean.name}" />

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{String.valueOf(bean.price)}" />

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{String.valueOf(bean.total)}" />

        <Button
            android:onClick="change1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="更改name和total"/>
        <Button
            android:onClick="change2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="更改price和total"/>
    </LinearLayout>
  1. 标注注解就是为了提供指定数据对应的UI的更新如果需求是全部更新可以不标注
  2. 继承于BaseObservable 会有几个公有方法
    notifyChange();代表的是全部更新
    notifyPropertyChanged(fileid); 这个fileid就是标注了@Bindable注解之后系统自动生成的没有的话就冲新build一下

完成上面的bean的书写等同于已经完成了数据绑定 不信? 有代码为证

public class Activity2 extends AppCompatActivity {

    private Bean2 bean2;
    private Activity3LayoutBinding binding3;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Activity2LayoutBinding binding = DataBindingUtil.setContentView(this, R.layout.activity2_layout);
        bean2 = new Bean2("apple", 1, 1);
        binding.setBean(bean2);

//        binding.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
//            @Override
//            public void onPropertyChanged(Observable sender, int propertyId) {
                //  todo 这里可以区别出不同的fileid
//            }
//        });

    }

    public void change1(View view) {
        bean2.setName("apple" + new Random().nextInt(10));
        bean2.setTotal(new Random().nextInt(100));

    }

    public void change2(View view) {
        bean2.setPrice(new Random().nextInt(10));
        bean2.setTotal(new Random().nextInt(100));
    }


2. 单向绑定之ObservableField

BaseObservable的方式更新数据有些麻烦优点就是可以指定刷新可是多数场景没有这个需求就可以用简单的 ObservableField
两种方式的主要区别依然是bean

public class Bean3  {
  private ObservableField<String> name;
  private ObservableField<Integer> price;
  private ObservableField<Integer> total;

    public ObservableField<String> getName() {
        return name;
    }

    public void setName(ObservableField<String> name) {
        this.name = name;
    }

    public ObservableField<Integer> getPrice() {
        return price;
    }

    public void setPrice(ObservableField<Integer> price) {
        this.price = price;
    }

    public ObservableField<Integer> getTotal() {
        return total;
    }

    public void setTotal(ObservableField<Integer> total) {
        this.total = total;
    }

可以看到直接用ObservableField指定泛型来代替原有的类型
注意! ! ! 不用继承任何类
除此之外没有任何不同

layout代码

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="bean"
            type="com.xiaoyu.jetpack.dataBinding.bean.Bean3" />
    </data>

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

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{bean.name}" />

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{String.valueOf(bean.price)}" />

        <TextView
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@{String.valueOf(bean.total)}" />

        <Button
            android:onClick="change3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="ObservableField更改数据"/>
    </LinearLayout>
</layout>

activity代码

public class Activity3 extends AppCompatActivity {

    private Bean3 bean3;
    private Activity3LayoutBinding binding3;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding3 = DataBindingUtil.setContentView(this, R.layout.activity3_layout);
        bean3 = new Bean3();
        bean3.setName(new ObservableField<String>("apple"));
        bean3.setPrice(new ObservableField<Integer>(10));
        bean3.setTotal(new ObservableField<Integer>(100));
        binding3.setBean(bean3);


    }


    public void change3(View view) {
        bean3.getName().set(new Random().nextInt(10) + "");
        bean3.getPrice().set(new Random().nextInt(30));
        bean3.getTotal().set(new Random().nextInt(100));
    }

}

看一下change3()这个方法有没有很别扭? 对没有看错设置数据的时候用的get()
而不是bean3.setName(new ObservableField<String>());好奇的我实验了一下 set的话是不现实更新数据的 ( 避坑各位 !! )

3. 双向数据绑定

双向数据绑定就是在ObservableField基础上进行的
只需要在xml文件里加入如下控件就可以实现了(Edittext在输入的时候显示name的控件也会跟着改变显示内容)

      <EditText
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="@={bean.name}" />

事件的绑定(类似于回调接口)

老三样:bean,activity,xml
--------------------------------bean--------------------------------


public class Bean4 {
private String name;
private String pwd;

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

--------------------------------activity--------------------------------

public class Activity4 extends AppCompatActivity {

    private Activity4LayoutBinding binding;
    private Bean4 bean4;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity4_layout);
        bean4 = new Bean4();
        bean4.setName("小明");
        bean4.setPwd("123");
        binding.setBean(bean4);
        binding.setClick(new BindClick());

    }
  //toast当前的name
    public class BindClick {
        public void ToastClick(Bean4 bean4) {
            Toast.makeText(Activity4.this, bean4.getName() + "", Toast.LENGTH_SHORT).show();
        }
    // 名字更改时更新bean
        public void afterTextChanged(Editable s) {
            bean4.setName(s.toString());
            binding.setBean(bean4);
        }
  //pwd更改时更新bean
        public void afterpwdChanged(Editable s) {
            bean4.setPwd(s.toString());
            binding.setBean(bean4);
        }
    }
}

--------------------------------xml--------------------------------

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="bean"
            type="com.xiaoyu.jetpack.dataBinding.bean.Bean4" />

        <variable
            name="click"
            type="com.xiaoyu.jetpack.dataBinding.activity.Activity4.BindClick" />
    </data>

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

        <Button
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:onClick="@{()->click.ToastClick(bean)}"
            android:text="Toast测试" />


        <TextView
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="@{bean.name,default = name}" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="@{bean.pwd,default = pwd}" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:afterTextChanged="@{click.afterTextChanged}"
            android:hint="name" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:afterTextChanged="@{click.afterpwdChanged}"
            android:hint="pwd" />

    </LinearLayout>
</layout>

先说 activity 设置好数据 再把数据给到xml没问题
为了方便创建了BindClick内部类里面有几个点击的方法(名字随便参数要准确)
跟到xml中发现点击事件()->click.ToastClick(bean) ( 这没啥说的吧 背下来可好 )
再看Edittext 的监听(改变监听) 有啥不一样? 参数没有了发现没 所以自己用的时候注意
还记得如何在xml文件中定义click事件不?有异曲同工之妙
同样的这种调用方式也适合调用工具类 ( 前提是要把工具类导入进来 )

数据传递与懒加载

这里的数据传递是xml之间的数据传递
懒加载的方式 ViewStub

1. 数据传递

因为databinding所有的数据都是要在xml中进行赋值,当我们使用include的时候的数据要怎么把数据传递过去呢

----------主页布局----------
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto">

    <data>

        <import type="com.xiaoyu.jetpack.dataBinding.bean.Bean5" />

        <variable
            name="bean"
            type="Bean5" />

    </data>

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

        <include
            layout="@layout/includ_ayout"
            bind:includeBean="@{bean}" />
    </LinearLayout>
</layout>


----------include的布局----------

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="com.xiaoyu.jetpack.dataBinding.bean.Bean5"/>
        <variable
            name="includeBean"
            type="Bean5" />
    </data>

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:gravity="center"
            android:text="@{bean.name}" />

    </LinearLayout>
</layout>

看似简单其实细节很重要总结了一下两点

  1. 在数据传递到include布局中时使用的 bind: nextname = "@{ ** }" nextname 的命名要和include中的name保持一致 (一定注意)
  2. include布局中也是要引入<variable>标签的

2. 懒加载ViewStub

这个懒加载细节性问题比较多因为粗心它浪费了我很多的时间

--------------------------------------主布局--------------------------------------
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="bean"
            type="com.xiaoyu.jetpack.dataBinding.bean.Bean5" />

    </data>

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

        <Button
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:onClick="showLayout"
            android:text="显示ViewStub" />

        <ViewStub
            android:id="@+id/view_stub"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout="@layout/stub_layout"
            bind:bean="@{bean}" />
    </LinearLayout>
</layout>
--------------------------------------StubView布局--------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.xiaoyu.jetpack.dataBinding.bean.Bean5" />
        <variable
            name="bean"
            type="Bean5" />
    </data>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="40dp"
        android:text="@{bean.name}" />
</layout>

activity代码(bean就不贴了,只有一个字段)

public class Activity5 extends AppCompatActivity {


    private static final String TAG = "ACTIVITY_5";
    private Activity5LayoutBinding binding;
    private Bean5 bean5;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity5_layout);
        bean5 = new Bean5();
        bean5.setName("小明");
        binding.setBean(bean5);
        binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
            @Override
            public void onInflate(ViewStub stub, View inflated) {
                //如果在 xml 中没有对 viewStub 进行数据绑定在此处绑定
                StubLayoutBinding stubBinding = DataBindingUtil.bind(inflated);
                stubBinding.setBean(bean5);
            }
        });
    }
    public void showLayout(View view){
        if (!binding.viewStub.isInflated()) { //只能被inflate一次
            binding.viewStub.getViewStub().inflate();
        }
    }
}

细节补充一下

  1. ViewStub可指定布局 数据的传递的点和上面的include一样
  2. 要通过binding.viewStubId.getViewStub().inflate()来进行加载但是注意只能加载一次否则会报错
  3. 传递数据可以通过xml中 bind: nextname = "@{ ** }" 也可以使用代码绑定
  4. 代码绑定 binding.viewStubId.setOnInflateListener() 之后通过 DataBindingUtil.bind(inflated); 获取的是ViewStub的databinding实例(一定注意!!)
  5. 如果有多个viewStub怎么办如何区分? 不同的viewStub是通过id区分的( 系统会根据id自动生成除去_的id我们直接 binding. 就可以找到 )

github地址

相关文章

网友评论

      本文标题:Databinding使用(java)

      本文链接:https://www.haomeiwen.com/subject/odykgctx.html