Android DataBinding的使用

一、介绍


本文是用来简记DataBinding使用前的配置及一些情况下的使用。

二、环境配置


在项目的要使用DataBinding的module的gradle构建文件里添加如下文件:

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

DataBinding插件将会在你的项目内添加必需提供的以及编译配置依赖。

三、简单使用DataBinding


1、layout文件


DataBinding layout文件与普通布局文件的不同点是:
根标签是 layout ,然后是一个 data 标签和一个ViewGroup标签,此ViewGroup即普通布局的根布局。如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.zf.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView 
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView 
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

data标签中声明了User类并指定了类对象,在当前layout中可以使用该类。
在layout中的使用方式:@{},如上面的:

android:text="@{user.firstName}"


2、数据类


在com.zf包下创建一个User类:

public class User {
   public final String firstName;
   public final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}


3、Binding数据


编译之后会在build\generated\source\apt\debug\项目包名\databinding中生成对应的Binding类。在上述为main_activity.xml,在MainActivity中使用会生成一个MainActivityBinding类,然后调用binding.setUser(user)就可以完成数据的刷新。生成binding的最简单方式是inflating。

在Java实化DataBinding风格xml布局与传统方式有所不同.

在Actvity中

private MainActivityBinding mBinding
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   mBinding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}

在自定义View和Fragment中

private FragmentMainBinding mBinding;

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
    @Nullable ViewGroup container,
    @Nullable Bundle savedInstanceState) {
        mBinding =DataBindingUtil.inflate(inflater,R.layout.fragment_main,container,false);
        return mBinding.getRoot();
}

在已经使用普通LayoutInfalter实例化的View上(xml必须是DataBinding风格的,普通LayoutInflater实例化布局时不会触发任何绑定机制,DataBindingUtil#bind才会发生绑定)

View view = LayoutInflater.from(context).inflate(R.layout.item_view,null,false);
ItemViewBinding binding = DataBindingUtil.bind(view);

四、layout文件


1、variable


在data中可以使用任意数量的variable元素,每个variable元素会在对应的Binding类中生成对应的set、get方法。

在编译时检查变量类型,因此如果变量实现Observable或observable collection,则应该在类型中反射。如果变量的基类或接口未实现Observable接口,则不会观察变量。

当存在用于各种配置的不同布局文件(例如,横向或纵向)时,组合变量。这些布局文件之间不得存在冲突的变量定义。

生成的绑定类对于每个描述的变量都有一个setter和getter。调用setter之前,变量采用托管代码默认值 – 引用类型为null,int为0,boolean为false等。

特殊变量context,可由绑定表达式生成。context的值是由根View的getContext()方法获取的Context对象。context变量由具有该名称的显式变量声明覆盖。

如下:

<data>
    <variable
        name="beanHairSideHome"
        type="cn.yanzijia.mine.model.HairSideHomeBean" />

</data>
public abstract void setBeanHairSideHome(@Nullable cn.yanzijia.mine.model.HairSideHomeBean BeanHairSideHome);
@Nullable
public cn.yanzijia.mine.model.HairSideHomeBean getBeanHairSideHome() {
    return mBeanHairSideHome;
}

2、import


可以在data中添加零个或多个import元素,引入的类可以在layout中直接使用。如下:

<data>
<import type="android.view.View" />
<variable
    name="beanHairSideHome"
    type="cn.yanzijia.mine.model.HairSideHomeBean" />
</data>

<cn.yanzijia.commonsdk.widget.CustomImageView
    android:id="@+id/mineHairSideFirstImage"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foreground="@drawable/mine_foreground_ffffff_8dp"
    android:visibility='@{beanHairSideHome.my_hair.img_list.isEmpty()?View.GONE:View.VISIBLE}' />

当类名有冲突的时候可以使用alias设置别名:

<import type="android.view.View"/>
<import type="com.zf.View" alias="myView"/>

在data中使用import导入的类型可以在variable中使用。

<data>
    <import type="com.zf.User"/>
    <import type="java.util.List"/>
    <variable name="userList" type="List&lt;User&gt;"/>
 </data>

警告:Android Studio尚未处理导入,因此导入变量的自动完成功能可能无法在IDE中运行。您的应用程序仍在编译,您可以通过在变量定义中使用完全限定名称来解决IDE问题。

还可以使用导入的类型来进行强转。以下示例将connection属性强制类型转换为User:

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

在表达式中引用静态字段和方法时,也可以使用导入的类型。以下代码导入MyStringUtils类并引用其capitalize方法:

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>


就像在托管代码中一样,java.lang.*会自动导入。

3、自定义Binding类名称


默认情况下,生成的Binding类的名称是布局文件去除下划线、(),所有单词首字母大写(被下划线分割的视为一个单词)。
然后再后面加上Binding。被放置在build\generated\source\apt\debug\项目包名\databinding包下。

自定义Binding类名称:
通过data标签后的class属性来进行重命名,如下:

<data class="MineBinding">
    ...
</data>


更换生成的Binding类所在的包:
直接在刚刚的指定的名称前指定包名就可以了。

<data class="com.zf.MineBinding">
    ...
</data>

4、includes


通过使用app命名空间和属性中的变量名,变量可以从包含的布局传递到包含的布局绑定中。使用如下:

home.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <import type="android.view.View" />
        <variable
            name="fragment"
            type="cn.yanzijia.poster.ui.fragment.HomeTabFragment" />

        <variable
            name="newOrderLeftTime"
            type="String[]" />

        <variable
            name="newOrderCreateTime"
            type="CharSequence" />

        <variable
            name="onlineInfoBean"
            type="cn.yanzijia.poster.model.HomeOnlineInfoBean.ResultBean" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <include
            layout="@layout/poster_layout"
            android:visibility="@{onlineInfoBean.order_new_num>0?View.VISIBLE:View.GONE}"
            app:bean="@{onlineInfoBean}"
            app:fragment="@{fragment}"
            app:newOrderCreateTime="@{newOrderCreateTime}"
            app:newOrderLeftTime="@{newOrderLeftTime}" />
    </LinearLayout>
</layout>


poster_layout.xml文件:

...
<data>

    <variable
        name="bean"
        type="cn.yanzijia.poster.model.HomeOnlineInfoBean.ResultBean" />
    <import type="android.view.View" />

    <import type="android.text.TextUtils" />

    <variable
        name="fragment"
        type="cn.yanzijia.poster.ui.fragment.HomeTabFragment" />

    <variable
        name="newOrderLeftTime"
        type="String[]" />

    <variable
        name="newOrderCreateTime"
        type="CharSequence" />

</data>
...

注意点:
include中添加的传递项在接收的xml文件中必须一一对应。
名称也必须相同。

数据绑定不支持include作为merge元素的直接子元素。例如,不支持以下布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="home" type="com.example.Home"/>
   </data>
   <merge><!-- Doesn't work -->
       <include layout="@layout/home"
           bind:user="@{home}"/>
   </merge>
</layout>
5、表达式


?? 符号:如果 ?? 左边的对象不为空则取左边的对象,否则取右边的对象。

android:text="@{user.firstName ?? user.lastName}"


三元运算符 ?:

android:text="@{user.firstName != null ? user.firstName : user.lastName}"


[] 符号:用来访问常用集合的值。

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String&gt;"/>
    <variable name="sparse" type="SparseArray&lt;String&gt;"/>
    <variable name="map" type="Map&lt;String, String&gt;"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"

注意:还可以使用object.key表示法引用map中的值。例如,**@{map[key]}**在上面的示例中可以替换为 @{map.key}。

字符串文字
可以使用单引号括起属性值,这允许在表达式中使用双引号,如以下示例所示:

android:text='@{map["firstName"]}'

也可以使用双引号来包围属性值。这时,字符串文字应该用后引号括起来`:

android:text="@{map[`firstName`]}"

可以通过提供参数来评估格式字符串和复数:

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

当复数采用多个参数时,应传递所有参数:

  Have an orange
  Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"


某些资源需要显式类型求值,如下表所示:

类型正常引用表达式引用
String[]@array@stringArray
int[]@array@intArray
TypedArray@array@typedArray
Animator@animator@animator
StateListAnimator@animator@stateListAnimator
color int@color@color
ColorStateList@color@colorStateList

访问resources资源
正常访问:

android:src="@{@drawable/poster_ic_fab_back_top}"

使用三元表达式:

android:src="@{islarge? @drawable/poster_ic_fab_back_top : @drawable/poster_ic_fab_back_top2}"
6、事件处理

数据绑定允许您编写从视图调度的表达式处理事件(例如,onClick()方法)。事件属性名称由监听器方法的名称确定,但有一些例外。例如,View.OnClickListener有一个方法onClick(),所以这个事件的属性是android:onClick。

对于click事件,有一些专门的事件处理程序需要除android:onClick避免冲突之外的属性。可以使用以下属性来避免这些类型的冲突:

ClassListener setterAttribute
SearchViewsetOnSearchClickListener(View.OnClickListener)android:onSearchClick
ZoomControlssetOnZoomInClickListener(View.OnClickListener)android:onZoomIn
ZoomControlssetOnZoomOutClickListener(View.OnClickListener)android:onZoomOut

可以使用以下机制来处理事件:

方法引用:在表达式中,可以引用符合监听器方法签名的方法。当表达式求值为方法引用时,Data绑定将方法引用和所有者对象包装在监听器中,并在目标view上设置该监听器。如果表达式求值为null,则数据绑定不会创建监听器并设置null监听器。
监听器绑定:这些是在事件发生时计算的lambda表达式。数据绑定总是创建一个监听器,它在view上设置。调度事件时,监听器会计算lambda表达式。
方法引用
事件可以直接绑定到处理程序方法,类似于android:onClick 可以分配给活动中的方法的方式。与View onClick属性相比的一个主要优点 是表达式在编译时处理,因此如果该方法不存在或其签名不正确,则会收到编译时错误。

方法引用和监听器绑定之间的主要区别在于,实际的监听器实现是在绑定数据时创建的,而不是在触发事件时创建的。如果希望在事件发生时评估表达式,则应使用监听器绑定。

要将事件分配给其处理程序,请使用普通绑定表达式,其值为要调用的方法名称。例如,考虑以下示例数据对象:

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

绑定表达式可以将View的单击监听器分配给 onClickFriend()方法,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

注意:表达式中方法的签名必须与监听器对象中方法的签名完全匹配。

监听器绑定
监听器绑定是在事件发生时运行的绑定表达式。它们类似于方法引用,但它们允许运行任意数据绑定表达式。适用于Gradle版本2.0及更高版本的Android Gradle插件提供此功能。

在方法引用中,方法的参数必须与事件监听器的参数匹配。在监听器绑定中,只有返回值必须与监听器的预期返回值匹配(除非它期望void)。例如,请考虑以下具有该onSaveClick() 方法的Presenter 类:

public class Presenter {
    public void onSaveClick(Task task){}
}

然后,您可以将click事件绑定到onSaveClick()方法,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout 
        android:layout_width="match_parent" 
        android:layout_height="match_parent">
        <Button 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content"
            android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

在表达式中使用回调时,数据绑定会自动创建必要的监听器并注册事件。当View触发事件时,数据绑定会计算给定的表达式。与常规绑定表达式一样,在计算这些监听器表达式时,仍然可以获得数据绑定的null和线程安全性。

在上面的示例中,我们尚未定义view传递给的参数onClick(View)。监听器绑定为监听器参数提供了两种选择:忽略方法的所有参数,也可以命名所有参数。如果为参数命名,则可在表达式中使用它们。例如,上面的表达式可以写成如下:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

或者,如果要在表达式中使用该参数,则可以按如下方式工作:

public class Presenter {
    public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

使用带有多个参数的lambda表达式:

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}

<CheckBox 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"
    android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果监听事件返回的类型不是void,则你的表达式也必须返回相同类型的值。例如,如果要监听长按事件,则应返回表达式boolean。

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果由于null对象而无法计算表达式,则数据绑定将返回该类型的默认值。例如:引用类型为null对于参考, int为0,boolean为false等。

如果需要使用带断言的表达式(例如:三元运算),则可以使用void。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

避免复杂的监听器
监听器表达式非常强大,可以使您的代码非常容易阅读。另一方面,包含复杂表达式的监听器使布局难以阅读和维护。这些表达式应该像将UI中的可用数据传递给回调方法一样简单。在从监听器表达式调用的回调方法中实现任何业务逻辑。


五、Data 对象


上述示例中,Data数据显示在UI上,但是改变Data属性UI不会刷新。DataBinding可以通过绑定使数据变化时UI随之刷新。数据变化的通知方式有三种:Observable objects,ObservableField,observable collection:观察对象、观察字段、观察集合。

1、Observable objects


ViewModel类:

public class User extends BaseObservable {
    public String firstName;
    public String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Bindable
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
        notifyPropertyChanged(BR.firstName);
    }

    @Bindable
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
        notifyPropertyChanged(BR.lastName);
    }
}

Acivity类中设置及切换数据:

private ActivityMainBinding mBinding;
private User user;
private String[] first = {"张", "周", "李"};
private String[] last = {"倩倩", "花花", "小小"};
private int i = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    user = new User("李","笑笑");
    mBinding.setUser(user);

}

public void changeData(View view) {
    if (i >= first.length) i = 0;
    user.setFirstName(first[i]);
    user.setLastName(last[i]);
    i++;
}

activity_main.xml文件:

<layout>
    <data>
        <variable
            name="user"
            type="com.example.zf.databindingdome.User"/>
    
    </data>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName+user.lastName}"
            android:textSize="20sp"
            android:textColor="#333333"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="20dp"/>
        <Button
            android:onClick="changeData"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Observable objects"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </android.support.constraint.ConstraintLayout>
</layout>

在 set 方法里面,使用 notifyPropertyChanged 来通知 UI 刷新,notifyPropertyChanged 只会刷新具体的值,notifyChange 方法则会刷新所有的值。
BR 的域则是通过在 get 方法上加 @Bindable 生成的。

注意:如果在layout文件中使用get方法获取ViewModel中的值,则改变ViewModel中的值的时候不会刷新UI。

2、ObservableField


ViewModel类:

public class UserField {
    public final ObservableField<String> userName=new ObservableField<>();
    public final ObservableField<String> userPassword=new ObservableField<>();
}

直接把属性定义成 ObservableField 类型的,并且修饰符需要是 public final 的。
除了 ObservableField,还可以使用 ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, ObservableParcelable。
访问值得时候,可以使用 get set 方法。

Activity中的处理:

private ActivityMainBinding mBinding;
private UserField userField;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    userField = new UserField();
    userField.userName.set("张三");
    userField.userPassword.set("123456");
    mBinding.setUserField(userField);

}

public void login(View view) {
    userField.userName.set("李四");
    userField.userPassword.set("098765");
}

activity_main.xml文件:

<layout>
    <data>
        <variable
            name="userField"
            type="com.example.zf.databindingdome.UserField"/>
    </data>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{userField.userName+userField.userPassword}"
            android:textSize="20sp"
            android:textColor="#333333"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="50dp"/>

        <Button
            android:onClick="login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ObservableField"
            android:layout_marginTop="80dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </android.support.constraint.ConstraintLayout>
</layout>

3、observable collection

如果使用 Map,List 等保存数据。DataBinding 也提供了 ObservableArrayMap,ObservableArrayList。
修改集合中的值对应UI的值也随之改变。

Activity中的处理:

private ObservableMap<String, Object> objectObservableMap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

    objectObservableMap = new ObservableArrayMap<>();
    objectObservableMap.put(Keys.name, "王五");
    mBinding.setMap(objectObservableMap);

}
public void collections(View view) {
    objectObservableMap.put(Keys.name,"赵六");
}

activity_main.xml文件:

<layout>
    <data>
        <import type="android.databinding.ObservableMap"/>
        <import type="com.example.zf.databindingdome.Keys"/>
        <variable
            name="map"
            type="ObservableMap&lt;String,Object>"/>
    </data>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{map[Keys.name]}"
            android:textSize="20sp"
            android:textColor="#333333"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="70dp"/>
     
        <Button
            android:onClick="collections"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="observable collection"
            android:layout_marginTop="160dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </android.support.constraint.ConstraintLayout>
</layout>

variable 类型声明的集合要添加泛型,但是泛型不能使用’<'字符。否则会报错:与元素类型 "variable" 相关联的 "type" 属性值不能包含 '<' 字符。
1
如果不使用泛型会报错:

Found data binding errors.
****/ data binding error ****msg:Cannot find the setter for attribute 'android:text' with parameter type V on android.widget.TextView.
file:E:\demo\DataBindingDome\app\src\main\res\layout\activity_main.xml
loc:46:28 - 46:41
****\ data binding error ****

可以使用’<'的转义字符:<其它特殊符号的转义字符:

&(逻辑与)  &amp;
<(小于)    &lt;
>(大于)    &gt;
"(双引号)  &quot;
'(单引号)  &apos;

六、Binding 类的生成


1、获取生成的 Binding 类的几个方法
DataBindingUtil.setContentView(Activity activity,int layoutId)
ActivityMainBinding mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

一般在Activity中使用

DataBindingUtil.inflate(LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)

一般用于Fragment或者Adapter中。

DataBindingUtil.bind(View root)
public class QuickDataBindingVH<T extends ViewDataBinding> extends BaseViewHolder {
    public T binding;
    public QuickDataBindingVH(ViewGroup parent, @LayoutRes int itemLayoutRes) {
        super(LayoutInflater.from(parent.getContext()).inflate(itemLayoutRes, parent, false));
    }
    public QuickDataBindingVH(View view) {
        super(view);
        binding=DataBindingUtil.bind(view);
    }
    public T getBinding() {
        return binding;
    }
}

RecyclerView封装的可使用的ViewHolder

2、Binding 的生成


view 的id
layout 中的每个带id的 View 在生成的Binding类中都会生成一个对应的 public final 成员变量。

<TextView
    android:id="@+id/tvUser"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user.firstName+user.lastName}"
    android:textSize="20sp"
    android:textColor="#333333"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    android:layout_marginTop="20dp"/>

会生成:

@NonNull
public final android.widget.TextView tvUser;

variables
每个variable都会在Binding类中生成对应的访问方法。

<data>
    <variable
        name="user"
        type="com.example.zf.databindingdome.User"/>
    <variable
        name="userField"
        type="com.example.zf.databindingdome.UserField"/>
</data>

会在Binding 类中生成setters和getters

public void setUserField(@Nullable com.example.zf.databindingdome.UserField UserField) {
    this.mUserField = UserField;
    synchronized(this) {
        mDirtyFlags |= 0x10L;
    }
    notifyPropertyChanged(BR.userField);
    super.requestRebind();
}
@Nullable
public com.example.zf.databindingdome.UserField getUserField() {
    return mUserField;
}

public void setUser(@Nullable com.example.zf.databindingdome.User User) {
    updateRegistration(3, User);
    this.mUser = User;
    synchronized(this) {
        mDirtyFlags |= 0x8L;
    }
    notifyPropertyChanged(BR.user);
    super.requestRebind();
}
@Nullable
public com.example.zf.databindingdome.User getUser() {
    return mUser;
}


executePendingBindings()方法
When a variable or observable object changes, the binding is scheduled to change before the next frame. There are times, however, when binding must be executed immediately. To force execution, use the executePendingBindings() method.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值