美文网首页
Databinding 基础入门

Databinding 基础入门

作者: Android绝世小菜鸟 | 来源:发表于2017-05-05 14:33 被阅读0次

    参考:
    官方文档
    Data Binding Guide(中文版 - Data Binding(数据绑定)用户指南)
    基于中文版的另一版教程

    整体概述:

    1.环境
    2.简单使用
        -- 1).布局文件定义
        -- 2).Data数据
        -- 3).Binding数据
    3.深入Layout
        -- 1)导入import
        -- 2) Variables详解
        -- 3)自定义Binding类名
        -- 4)includes使用
        -- 5)常用表达式
    4.Data对象
        -- 1)Observable对象
        -- 1)Observable字段
        -- 1)Observable集合
    5.Binding生成
        -- 1) 带 ID 的 View
        -- 2)ViewStubs 
        -- 3) Dynamic Variables(RecycleView使用)
    6 属性Setters
    7.转换器 (Converters) 
    8.绑定事件处理方法
    9.BindingAdapter的使用场景
    10.双向绑定
    

    1.环境

    Android Studio的Data Binding插件需要Android Studio 1.3.0 或 更高版本。
    
    android {
        ....
        dataBinding {
            enabled = true    
        }    
    }
    

    2.简单使用

    1).布局文件定义
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
          <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}"/>
          <TextView android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="@{user.lastName}"/>
      </LinearLayout>
    </layout>
    

    user变量属性 type为引用的Data对象或者其他对象

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

    属性引用

    @{user.firstName} 
    @{user.lastName}
    
    2)Data数据
    Pojo对象
    public class User {
       public final String firstName;
       public final String lastName;
       public User(String firstName, String lastName) {
           this.firstName = firstName;
           this.lastName = lastName;
       }
    }
    
    Javabeans对象
    public class User {
       private final String firstName;
       private final String lastName;
       public User(String firstName, String lastName) {
           this.firstName = firstName;
           this.lastName = lastName;
       }
       public String getFirstName() {
           return this.firstName;
       }
       public String getLastName() {
           return this.lastName;
       }
    }
    

    注意:

    Pojo对象特点:一旦拥有数据,不会改变
    JavaBeans对象:可以自定义数据输出,在DataBinding中,实际获取的值是get属性方法,如果不使用自定义get方法,默认Pojo和JavaBeans对象是一样的。(不建议同时使用,否则会出现数据不一致的情况)

    3)Binding数据

    默认情况下,一个Binding类会基于layout文件的名称而产生,将其转换为Pascal case(译注:首字母大写的命名规范)并且添加“Binding”后缀。上述的layout文件是main_activity.xml,因此生成的类名是MainActivityBinding。此类包含从layout属性到layout的Views中所有的bindings(例如user变量),并且它还知道如何给Binding表达式分配数值。创建bindings的最简单的方式是在inflating(译注:layout文件与Activity/Fragment的“链接”)期间如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
    
    //1.直接使用绑定并setContentView
       MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
       如想获取View对象,可使用
       MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
    
    
    //2.ListView或者RecyclerView adapter使用Data Binding时
       ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
       如想获取View对象,可使用
       ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
    false);
    
    
    
       User user = new User("Test", "User");
       binding.setUser(user);
    }
    

    建议分开绑定,当使用不同的机制载入layout时,使用下面方式:

    MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
    

    3.深入Layout

    1)导入import

    注意:java.lang.* 包中的类会被自动导入,可直接使用

    <data>
        <import type="android.view.View"/>
    </data>
    
    <TextView
       android:text="@{user.lastName}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
    

    当类名有冲突时,可以让其中一个加一个别名来使用:

    <import type="android.view.View"/>
    <import type="com.example.real.estate.View"  alias="Vista"/>
    

    使用方法建议:

    1.当只是使用类Static属性和方法,不需要额外再设置属性,可直接使用以下方式

    <import type="com.example.real.estate.View"  alias="Vista"/>
    

    2.当需要使用类所有属性和方法,并需要设置属性,例如model数据,使用以下方式。

    <variable
                name="pageNew"
                type="com.gson.HomePageNew"/>
    或者
    <import type="com.gson.HomePageNew">
    <variable   name="pageNew"
                      type="HomePageNew"/> 
    
    2) Variables详解:

    在data中可以使用任意数量的variable元素。每一个variable元素描述了一个用于layout文件中Binding表达式的属性。

    <data>
        <import type="android.graphics.drawable.Drawable"/>
        <variable name="user"  type="com.example.User"/>
        <variable name="image" type="Drawable"/>
        <variable name="note"  type="String"/>
    </data>
    

    注意:

    • 1.Variables类型在编译时被检查,如果一个变量实现了可观察的或可观察到的集合,就应该在类型中反映粗来,如果这个变量是一个类/接口,但没有实现可观察的接口eg:BaseObservable,那么该变量不会被观察到。
    • 2.当对于多种配置有不同的layout文件时(如,横向或纵向),Variables会被合并。这些layout文件之间必须不能有冲突的Variable定义。
    • 3.每一个Binding类中属性,都会有一个默认的属性eg:null(引用) 0(int)false(boolean) ,直到setter调用。
    3)自定义Binding类名

    一般直接使用layout对应的名称

    <data class="ContactItem">
        ...
    </data>
    
    4)includes

    通过使用application namespace以及在属性中的Variable名字从容器layout中传递Variables到一个被包含的layout:

    1.上一层Layout
    <?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="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <include layout="@layout/name"
               bind:user="@{user}"/>
           <include layout="@layout/contact"
               bind:user="@{user}"/>
       </LinearLayout>
    </layout>
    
    2.下一层layout
    <?xml version="1.0" encoding="utf-8"?>
    <layout>
        <data>
            <import type="android.view.View"></import>
            <variable
                name="user"
                type="com.example.User"></variable>
        </data>
        。。。。
    </layout>
    

    注意:在name.xml以及contact.xml两个layout文件中必需要有user variable

    5)常用表达式

    数学 + - / * %
    字符串连接 +
    逻辑 && ||
    二进制 & | ^
    一元运算 + - ! ~
    移位 >> >>> <<
    比较 == > < >= <=
    instanceof
    分组 ()
    null
    Cast
    方法调用
    数据访问 []
    三元运算 ?:

    eg:

    android:text="@{String.valueOf(index + 1)}"
    android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
    android:transitionName='@{"image_" + id}'
    
    • 不支持属性:

    this
    super
    new

    显式泛型调用

    • Null合并操作
      android:text="@{user.displayName ?? user.lastName}"
                      ||等价  
      android:text="@{user.displayName != null ? user.displayName : user.lastName}"
    
    
    • 属性引用
    android:text="@{user.lastName}"
    
    • 集合使用
    <data>
      <import type="android.util.SparseArray"/>
      <import type="java.util.Map"/>
      <import type="java.util.List"/>
      <variable name="list" type="List<String>"/>
      <variable name="sparse" type="SparseArray<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="@{sparse[index]}"
    …
    android:text="@{map[key]}"
    
    • 字符串使用:

      建议使用这种方式

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

    双引号包含属性(缺点:不能使用 ”app“ + ”name“ 的形式拼接

    android:text="@{map[`firstName`]}"
    android:text="@{map["firstName"]}"
    
    • Resources的使用:
    android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
    
    • 格式化字符串
    android:text="@{@string/nameFormat(firstName, lastName)}"
    <string name="nameFormat">%s, %s</string>
    

    当复数需要多个参数时

    Strings类
     <plurals name="orange">
            <item quantity="one">Have an orange</item>
            <item quantity="other">Have %d oranges</item>
      </plurals>
    xml:  
    android:text="@{@plurals/orange(orangeCount, orangeCount)}"
    
    • 一些资源需要明确类型调用。
    Type【类型】 Normal Reference【正常引用】 Expression Reference【表达式引用】
    String[] @array @stringArray
    int[] @array @intArray
    TypedArray @array @typedArray
    Animator @animator @animator
    StateListAnimator @animator @stateListAnimator
    colorint @color @color
    ColorStateList @color @colorStateList

    4.Data对象----->ObservableActivity.java

    任何Plain old Java object(PO​​JO)可用于Data Binding,但修改POJO不会导致UI更新。Data Binding的真正能力是当数据变化时,可以通知给你的Data对象。有三种不同的数据变化通知机制:
    Observable对象ObservableFields以及observable collections

    当这些可观察Data对象​​绑定到UI,Data对象属性的更改后,UI也将自动更新。

    1) Observable对象:

    实现android.databinding.Observable接口的类可以允许附加一个监听器到Bind对象以便监听对象上的所有属性的变化。

    Observable接口有一个机制来添加和删除监听器,但通知与否由开发人员管理。为了使开发更容易,一个BaseObservable的基类为实现监听器注册机制而创建。Data实现类依然负责通知当属性改变时。这是通过指定一个Bindable注解给getter以及setter内通知来完成的。(注意在get方法上加上@Bindable注释,同时记得给xml中对应的binding赋值,eg: binding.setResult(scanResult);)

    private static class User extends BaseObservable {
       private String firstName;
       private String lastName;
       @Bindable
       public String getFirstName() {
           return this.firstName;
       }
       @Bindable
       public String getFirstName() {
           return this.lastName;
       }
       public void setFirstName(String firstName) {
           this.firstName = firstName;
           notifyPropertyChanged(BR.firstName);
       }
       public void setLastName(String lastName) {
           this.lastName = lastName;
           notifyPropertyChanged(BR.lastName);
       }
    }
    

    在编译期间,Bindable注解在BR类文件中生成一个EntryBR类文件会在模块包内生成。如果用于Data类的基类不能改变,Observable接口通过方便的PropertyChangeRegistry来实现用于储存和有效地通知监听器。

    2) Observable 字段

    一些小工作会涉及到创建Observable类,因此那些想要节省时间或者几乎没有几个属性的开发者可以使用ObservableFieldsObservableFields是自包含具有单个字段的observable对象。它有所有基本类型和一个是引用类型。要使用它需要在data对象中创建public final字段:

    private static class User {
       public final ObservableField<String> firstName =
           new ObservableField<>();
       public final ObservableField<String> lastName =
           new ObservableField<>();
       public final ObservableInt age = new ObservableInt();
    }
    

    使用方法:

    user.firstName.set("Google");
    int age = user.age.get();
    
    3)Observable 集合

    系统为我们提供了所有的 primitive type 所对应的Observable类,eg:ObservableIntObservableFloatObservableBooleanObservableField 对应着 reference type

    ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
    user.put("firstName", "Google");
    user.put("lastName", "Inc.");
    user.put("age", 17);
    

    在layout文件中,通过String键可以访问map:

    <data>
        <import type="android.databinding.ObservableMap"/>
        <variable name="user" type="ObservableMap<String, Object>"/>
    </data>
    …
    <TextView
       android:text='@{user["lastName"]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user["age"])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    

    ObservableArrayList用于键是整数:

    ObservableArrayList<Object> user = new ObservableArrayList<>();
    user.add("Google");
    user.add("Inc.");
    user.add(17);
    

    在layout文件中,通过索引可以访问list:

    <data>
        <import type="android.databinding.ObservableList"/>
        <import type="com.example.my.app.Fields"/>
        <variable name="user" type="ObservableList<Object>"/>
    </data>
    …
    <TextView
       android:text='@{user[Fields.LAST_NAME]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    <TextView
       android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    

    5.Binding生成

    1) 带 ID 的 View

    Data Binding 有效降低了代码的冗余性,甚至完全没有必要再去获取一个 View实例,但是情况不是绝对的,万一我们真的就需要了呢?不用担心,只要给View定义一个 ID,Data Binding 就会为我们生成一个对应的 final 变量。

    <TextView
        android:id="@+id/firstName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    

    上面代码中定义了一个 ID 为 firstNameTextView,那么它对应的变量就是

    public final TextView firstName;
    
    2)ViewStubs -----> ViewStubActivity.java

    ViewStubs跟正常的Views略有不同。他们开始时是不可见的,当他们要么设置为可见或被明确告知要载入时,它们通过载入另外一个layout取代了自己。

    由于ViewStub基本上从View的层次结构上消失,在Binding对象的View也必须消失来允许被收集。因为Views是最后的,一个ViewStubProxy对象取带ViewStub,给开发者获得了ViewStub,当它存在以及还可以访问载入的View层次结构时当ViewStub已被载入时。

    当载入另一个layout,为新的布局必需创建一个Binding。因此,ViewStubProxy必需监听ViewStub的OnInflateListener监听器并在那个时候建立Binding。因为只有一个可以存在,ViewStubProxy允许开发者在其上设置一个OnInflateListener它会在建立Binding后调用。

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <LinearLayout
            ...>
            <ViewStub
                android:id="@+id/view_stub"
                android:layout="@layout/view_stub"
                ... />
        </LinearLayout>
    </layout>
    

    //必须在设置监听后去设置binding

    binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
    binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {
       @Override
       public void onInflate(ViewStub stub, View inflated) {
           ViewStubBinding binding = DataBindingUtil.bind(inflated);
           User user = new User("fee", "lang");
           binding.setUser(user);
       }
    });
    
    3) Dynamic Variables---->DynamicActivity.class

    以 RecyclerView 为例,Adapter 的 DataBinding 需要动态生成,因此我们可以在 onCreateViewHolder 的时候创建这个 DataBinding,然后在 onBindViewHolder 中获取这个 DataBinding。

    public static class BindingHolder extends RecyclerView.ViewHolder {
        private ViewDataBinding binding;
    
        public BindingHolder(View itemView) {
            super(itemView);
        }
    
        public ViewDataBinding getBinding() {
            return binding;
        }
    
        public void setBinding(ViewDataBinding binding) {
            this.binding = binding;
        }
    }
    
    @Override
    public BindingHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        ViewDataBinding binding = DataBindingUtil.inflate(
                LayoutInflater.from(viewGroup.getContext()),
                R.layout.list_item,
                viewGroup,
                false);
        BindingHolder holder = new BindingHolder(binding.getRoot());
        holder.setBinding(binding);
        return holder;
    }
    
    @Override
    public void onBindViewHolder(BindingHolder holder, int position) {
        User user = users.get(position);
        holder.getBinding().setVariable(BR.user, user);
        holder.getBinding().executePendingBindings();
    }
    

    注意此处 DataBindingUtil 的用法:

    ViewDataBinding binding = DataBindingUtil.inflate(
        LayoutInflater.from(viewGroup.getContext()),
        R.layout.list_item,
        viewGroup,
        false);
    

    6 属性Setters

    有了Data Binding,即使属性没有在declare-styleable
    中定义,我们也可以通过 xml 进行赋值操作。 为了演示这个功能,自定义 View -NameCard,属性资源R.styleable.NameCard中只定义了一个age属性,其中firstNamelastName只有对应的两个setter方法。
    只要有setter方法就可以像下面代码一样赋值:

    <com.liangfeizc.databindingsamples.attributesetters.UserView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="@dimen/largePadding"
        app:onClickListener="@{activity.clickListener}"
        app:firstName="@{@string/firstName}"
        app:lastName="@{@string/lastName}"
        app:age="27" />
    

    7.转换器 (Converters) ----->ConversionsActivity.java

    使用Converter一定要保证它不会影响到其他的属性,例如这个@BindingConversion
    -convertColorToString就会影响到android:visibility, 因为他们都是都符合从 int 到 int 的转换。

    在 xml 中为属性赋值时,如果变量的类型与属性不一致,通过DataBinding可以进行转换。
    例如,下面代码中如果要为属性android:background赋值一个int型的 color 变量:

    <View
      android:background="@{isError.get() ? @color/red : @color/white}"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:layout_height="@{height}" />
    

    只需要定义一个标记了 @BindingConversion 的静态方法即可(方法的定义位置可以随意):

    @BindingConversion
    public static ColorDrawable convertColorToDrawable(int color) {
        return new ColorDrawable(color);
    }
    

    8.绑定事件处理方法

    9. BindingAdapter的使用场景

    10.双向绑定

    用法举例

    很简单,在要使用双向绑定的地方,使用 “@={}” 即可。

    <EditText android:text="@={user.firstName}" />
    

    firstName 必须是 ObservableField <T> 类型

    适用范围

    双向绑定只适用于那些某个属性绑定监听事件的控件,如

    • TextView/EditView/Button (android:text, TextWatcher)
    • CheckBox (android:checked, OnCheckedChangeListener)
    • DatePicker(android:year, android:month, android:day, OnDateChangedListener)
    • TimePicker(android:hour, android:minute, OnTimeChangedListener)
    • RatingBar(android:rating, OnRatingBarChangeListener)

    相关文章

      网友评论

          本文标题:Databinding 基础入门

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