DataBinding 完整使用介绍篇


【前言】

    DataBindinggoogle2015I/O大会上推出的一个数据绑定框架。提高了开发者的开发效率,具有高性能、功能强等特点。DataBinding是基于MVVM模式,保证了xml中的数据唯一来源为ViewModel,去掉了ActivityFragment中大部分UI相关代码,减少了findViewById的调用,保证UI更新执行在主线程。

【项目源码】

https://github.com/JAYAndroid/DataBindingDemo.git

【环境构建】

     Data Binding Library兼容到了Android2.1版本。要求Gradle版本不低于1.5 Android Studio至少1.3

要使用DataBinding,需要在项目modelbuild.gradle里加上如下设置:

   android{

   ...

dataBinding{
    enabledtrue;
}

}

【布局差异】

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

<data>

    <variable

        name=””

        Type=””/>

 

        <importtype="android.view.View"alias="otherName"/>

</data>

    

<!-- 此次放正常的布局 -->

</layout>

 

 

    可以看出,使用了DataBindingxml布局,根元素变成了<layout>,里面的<data>嵌套了<variable>,用于声明传递进来的Object变量名和类型。此外还可以使用<import>导入Object,就像在java代码中import一样。如果出现导入的多个Object具有相同的名称,则可以通过alias属性设置别名。如上,otherName也可以代表android.view.View

【类名自动生成规则】

    每个支持DataBindingxml对应一个自动生成的Binding类。生成规则是,xml布局文件名称去掉下划线,根据驼峰标识,再加上Binding。如content_main.xml对应ContentMainBinding.java。当然如果需要自定义类名,可以在<data>标签上设置class属性值,如:

 

<!--注意,name和其他布局里面的name不能一样, 用class自定义生成的Binding classes 类名,也可

以使用全路径名来指定生成类存放的位置-->

<data class="CustomClassName">

 

【绑定xmlActivity

    只需一行代码就可以绑定。通过调用DataBindingUtilsetContentView获取自动生成DataBinding类。

  CustomClassName viewDataBinding = DataBindingUtil.setContentView(this,R.layout.content_main);  

 

【不用findViewById

    布局里有个TextView控件,idtv_no_find_biew_by_id

<TextView
    android:id="@+id/tv_no_find_biew_by_id"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textColor="#0f0"
    android:textSize="16sp"
    tools:text="不用 findviewbyid 了"/>

    可通过viewDataBinding对它进行访问操作。

viewDataBinding.tvNoFindBiewById.setText("不用执行 findViewById 了");

【绑定Object

    创建一个JavaBean User,注意需要提供getXXX。如果当User的属性name变化了需要实时更新UI,那么User需要extends BaseObservable,并且在getName增加@Bindable,在setName调用

notifyPropertyChanged(BR.name)

public classUserextendsBaseObservable{
    privateStringname;
    private Stringage;
    ...
    @Bindable
    publicStringgetName() {
        returnname;
    }

    public voidsetName(String name) {
        this.name= name;
        /**
         * 通知更新UI界面
         */
        
notifyPropertyChanged(BR.name);
    }

    然后在xml中声明:

<variable
    name="user"
    type="com.example.jay.databindingdemo.User"/>

    注意,type的值是对应类的全路径名。然后就可以在xml中使用user,如下:

<TextView
    android:id="@+id/tv_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/tv_tip"
    android:text="@{user.name}"
    android:textSize="18sp"/>

    说明:@{user.name} 调用了User中的getName,如果没有提供getxxx是无法访问User中的name属性。

mUser =newUser("小明","33", true);
viewDataBinding.setUser(mUser);

    调用了以上代码后,就可以在xml中获取username值了。

【绑定点击事件】

    点击事件的绑定有两种方式:

    ① Method References

    声明事件处理类:

public classFirstClickHandle {

    /**
     * 注意:函数的签名必须和原来的 Listener 函数签名一致,但是方法名可以自定义
     * 例:OnClickListener 中的 void onClick(View v);
     *
     *
@paramview
     */
    
public voidonClickFirst(View view) {
        Toast.makeText(view.getContext(),"第一种点击绑定:方法引用",Toast.LENGTH_LONG).show();
    }
}

 

    xml中声明:

<variable
    name="firstClickHandler"
    type="com.example.jay.databindingdemo.FirstClickHandle"/>

    Button方法引用:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{firstClickHandler::onClickFirst}"
    android:text="Method References 绑定点击事件"
    android:textAllCaps="false"/>

    注:可以写成firstClickHandler::onClickFirst,也可以是firstClickHandler.onClickFirst

最后记得FirstClickHandleViewDataBinding绑定:

FirstClickHandle firstClickHandle =newFirstClickHandle();
viewDataBinding.setFirstClickHandler(firstClickHandle);

Listener Bindings

    该方法的实现依赖lambda 表达式,可以灵活的指定函数的参数,函数的签名不需要和来的Listener函数签名一致,但是返回值必须一样,除非返回值是void

声明事件处理类:

public classSecondClickHandle {

    /**
     * 函数的签名不需要和来的 Listener 函数签名一致,但是返回值必须一样,除非返回值是void
     *
     *
@paramview
     *
@paramuser
     */
    
public voidonClickSecond(View view,User user) {
        Toast.makeText(view.getContext(),"xml传递来的user =" + user.getName(),Toast.LENGTH_LONG).show();
    }
}

    xml中声明:

<variable
    name="secondClickHnalder"
    type="com.example.jay.databindingdemo.SecondClickHandle"/>

    Button通过lambda表达式引用:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:onClick="@{(theview) -> secondClickHnalder.onClickSecond(theview, user)}"
    android:text="Listener Bindings 绑定点击事件"
    android:textAllCaps="false"/>

    最后,别忘了添加如下的代码:

 SecondClickHandle secondClickHandle =newSecondClickHandle();
 viewDataBinding.setSecondClickHnalder(secondClickHandle);

    说明:lambda 表达式支持从xml回传参数到调用者。此次的theView代表原始方法onClick(View view)中的view,名称可自定义。

【绑定数据到子布局】

    首先声明命名空间bind

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

  然后在标签<include>中使用bind:xxx=”@{user}”,将数据“传递”进去。

<!--注意,使用了bind的include标签的父标签,不能是 <merge>。而且child_layout中的变量名必须和 bind:xxx的xxx一致 -->
<include
    layout="@layout/child_layout"
    bind:child="@{user}"/>

    在子布局中如下:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="child"
            type="com.example.jay.databindingdemo.User"/>
    </data>

    注:① 使用了bindinclude的父标签不能是<merge>,如下所示是错误的:

<merge>
    <include layout="@layout/name"
        bind:user="@{user}"/>
</merge>

    ② 子布局中的variablename必须和父布局中bind:xxxxxx一致,如上面父布局的xxxchild,所以子布局中的name也是child

【自动更新UI

    用来显示User.name TextView控件,如果name值发生变化,DataBinding支持动态更新UI,而且开发者无需关系UI线程切换问题。

首先,在User中的getName上增加@Bindable,并且在setName中设置完name值后,调用notifyPropertyChanged(BR.name)。前提是User extends BaseObservable

 @Bindable
 publicStringgetName() {
    returnname;
}
 public voidsetName(String name) {
    this.name= name;
    /**
     * 通知更新UI界面
     */
    
notifyPropertyChanged(BR.name);
} 

    当调用了mUser.setName会自动更新UI。当然如果在子线程调用setName也是可以的,DataBinding会自动处理,并更新UI

       public voidafterTextChangeCall(finalCharSequence s, intstart, intbefore, intcount) {
            /**
             * 自动更新UI,不用关心线程切换问题
             */
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    mUser.setName(s.toString());
//                }
//            }).start();

            mUser.setName(s.toString());
        }

【支持使用表达式】

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/tv_name"
    android:text="成年人"
    android:textSize="18sp"
    android:visibility="@{user.adult ? View.VISIBLE : View.GONE}"/>

    以上使用到了三元运算符表达式。

    表达式中支持的操作符有:

数字运算符

+ - / * %

字符串连接符

+

逻辑运算符

&& ||

二进制位操作符

& | ^

一元运算符

+ - ! ~

位移操作符

 >> >>> <<

比较运算符

 == > < >= <=

实例运算符

instanceof

集合操作

Grouping ()

实例强制转换

Cast

方法调用

Method calls

属性访问

Field access

数组访问

Array access [

三元运算符

?:

    同时要注意,表达式中不支持的有:This newsuper

【高级绑定——双向绑定】

    双向绑定指的是,在某处改变了属性的值,其他用到这个属性值的地方,也会同步更新。双向绑定的符号是 @=,如下:

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="双向绑定"
    android:text="@={user.age}"/>

    其它地方的控件TextView也使用了user.age

<TextView
    android:hint="双向绑定内容"
    android:text="@{@string/welcome(user.age)}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

    当用户在EditText输入内容时,TextView的内容会实时更新。

注:UsergetAge需要加上@Bindable,并且在setAge里调用notifyPropertyChanged(BR.age)

 

【高级绑定——Adapter

    DataBinding除了支持Event BindingData Object Binding,还支持Dynamic Variables(暂且翻译成动态变量绑定)。如在RecyclerView.Adapter中的onBindViewHolder

首先,声明DataBindingViewHolder,让它继承RecyclerView.ViewHolder:并提供getBinding方法

public classDataBindingViewHolder<TextendsViewDataBinding>extendsRecyclerView.ViewHolder {
    privateTbinding;

    public DataBindingViewHolder(Tbinding) {
        super(binding.getRoot());
        this.binding= binding;
    }

    publicTgetBinding() {
        returnbinding;
    }
}

    然后,声明DataBindingAdapter,让它继承RecyclerView.Adapter,注意它复写的两个方法onCreateViewHolderonBindViewHolder

@Override
public DataBindingViewHolderonCreateViewHolder(ViewGroup parent, intviewType) {
    ViewDataBinding binding = DataBindingUtil.inflate(mLayoutInflater,R.layout.recycle_item,parent, false);
    return new DataBindingViewHolder(binding);
}

@Override
public void onBindViewHolder(DataBindingViewHolder holder, intposition) {
    holder.getBinding().setVariable(BR.item,dataSources.get(position));
    holder.getBinding().executePendingBindings();
}

    说明:onCreateViewHolder中,通过DataBindingUtil.inflate指定需要绑定的xml布局。在onBindViewHolder中,通过setVariable绑定数据到动态变量,并通过调用executePendingBindings来强制实时更新UI

【总结】

    DataBinding带来便利的同时,如果在xml过度使用DataBinding,会降低xml的可读性。同时需要注意表达式不要过于复杂,分清xmlJava的分界。

本文对DataBinding的内容进行了取舍,有些特性没有讲解到,如自定义属性、表达式连、动画等。感兴趣的朋友们,请自行查阅资料学习(建议参考官方开发者文档进行学习!)

 

 

 

 

 

 

发布了6 篇原创文章 · 获赞 8 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览