zoukankan      html  css  js  c++  java
  • Data Binding Guide——google官方文档翻译(下)

    这篇博客是Data Binding Guide官网文档翻译的下篇。假设没看过前半部分翻译的能够先看Data Binding Guide——google官方文档翻译(上)

     一,数据对象

    不论什么不含业务逻辑的java简单对象(POJO)可用于数据绑定。但改动POJO不能使UI更新。而通过数据绑定能够使数据对象感知到数据的变化。有三种不同的感知数据改变的机制,可见对象,可见字段,和可见集合。

    当一个可见数据对象绑定到用户界面和数据对象变化的属性时,用户界面将自己主动更新。

     可见对象

    一个实现可见接口的类,同意把监听器和对象绑定,以便监听该对象的全部属性的变化。

    可见接口有加入和删除侦听器的机制,但数据改变的通知机制取决于开发人员。

    为了使开发更简洁,创建一个基类(BaseObservable)来实现监听器注冊机制。当属性改变时,数据实现类仍负责告知机制。当中getter方法加@Bindable凝视。并在setter方法中告知属性的变化。

    private static class User extends BaseObservable {
       private String firstName;
       private String lastName;
       @Bindable
       public String getFirstName() {
           return this.firstName;
       }
       @Bindable
       public String getLastName() {
           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);
       }
    }
    在编译过程中,绑定凝视在BR类文件里生成一个条目。然后在模块包中生成BR类文件。假设数据类的基类不可改变,能够使用方便PropertyChangeRegistry有效地存储和通知侦听器的方式来实现可见接口。

     可见字段

    创建可见类的过程中,开发人员想节省时间或有非常多属性时能够使用可见字段,比如一些经常使用的可见类型字段:ObservableBooleanObservableByteObservableChar,ObservableShortObservableIntObservableLongObservableFloatObservableDouble, andObservableParcelable

    可见字段是有独立字段的可见对象。原始版本号在訪问操作中避免装箱和拆箱操作。为方便使用,在数据类创建使用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();
    }
    通过setter和getter方法获取值。

    user.firstName.set("Google");
    int age = user.age.get();

    可见集合

     一些应用程序使用很多其它的动态结构来保存数据。

    可见集合支持键控存取方式訪问这些数据对象。当键是一个像字符串这样的引用类型时,可使用ObservableArrayMap。

    ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
    user.put("firstName", "Google");
    user.put("lastName", "Inc.");
    user.put("age", 17);
    在布局中,能够通过引用String类型的键进行映射:

    <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);
    在布局中。可通过索引訪问列表。

    <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"/>

    生成绑定

    不论什么不含业务逻辑的java简单对象(POJO)可用于数据绑定,但改动POJO不能使UI更新。

    而通过数据绑定能够使数据对象感知到数据的变化。

    有三种不同的感知数据改变的机制,可见对象,可见字段。和可见集合。

    当一个可见数据对象绑定到用户界面和数据对象变化的属性时,用户界面将自己主动更新。

    生成绑定类链接在布局视图的布局变量。正如前面所讨论的,绑定的名称和包能够自己定义。

    生成的绑定类都继承ViewDataBinding。

    应该马上创建绑定。以确保布局中的表达式与视图的绑定不干扰视图层。有几种绑定到布局的方法。

    最常见的是在绑定类调用静态方法inflate。

    inflate解析视图,并完毕数据绑定。

    另一个更简单的版本号。仅仅须要在inflate方法中引入LayoutInflater或再加上ViewGroup:

    MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
    MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);

    假设布局inflate机制有变化,也能够使用分开绑定的机制。

    MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
    在无法预先知道是否绑定的情况下,能够使用DataBindingUtil 类创建绑定:

    ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
        parent, attachToParent);
    ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

    ID的视

    在布局中每一个有ID的视图将相应生成一个public final字段的变量,绑定类仅仅调用一次布局,并创建每一个视图。这样的机制比调用findViewById方法更有效率。

    <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}"
       android:id="@+id/firstName"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"
      android:id="@+id/lastName"/>
       </LinearLayout>
    </layout>
    会生成带有例如以下属性的绑定类:

    public final TextView firstName;
    public final TextView lastName;
    ID可能没有数据绑定那样必要,可是仍有一些实例证明,从代码訪问角度看仍然是必要的。

    二、变量

    每一个变量会给出存取方法:

    <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>
    在绑定中生成settergetter方法:

    public abstract com.example.User getUser();
    public abstract void setUser(com.example.User user);
    public abstract Drawable getImage();
    public abstract void setImage(Drawable image);
    public abstract String getNote();
    public abstract void setNote(String note);

    ViewStubs

    viewstubs与普通视图不同。

    他们開始是是一个不可视而且大小为0的视图。能够延迟到运行时填充布局资源。设置为Visible或调用inflate()之后。就会填充布局资源。ViewStub便会被填充的视图替代。

    因为ViewStub会在视图层消失,为了正常的连接,相应的绑定对象也要随之消失。因为视图层是final类型的,ViewStubProxy对象替代ViewStub 后,开发人员能够訪问 ViewStub。而且当 ViewStub 在视图层中被载入时,开发人员也能够訪问载入的视图。

    当解析其它布局时,新的布局中要建立绑定关系。

    因此,ViewStubProxy 对象要监听ViewStub的OnInflateListener并建立绑定。开发人员能够在ViewStubProxy 对象上建立一个OnInflateListener,绑定建立后。便可调用OnInflateListener。

    高级绑定

    动态变量

    有时详细的绑定类会不被识别。比如,操作随意布局的RecyclerView.Adapter必须在onBindViewHolder中指定绑定值,才可识别出相应的绑定类。

    在这个样例中,RecyclerView绑定的全部布局都有一个item变量。BindingHolder 对象通过引用getBinding() 方法获取 ViewDataBinding 基类。

    public void onBindViewHolder(BindingHolder holder, int position) {
       final T item = mItems.get(position);
       holder.getBinding().setVariable(BR.item, item);
       holder.getBinding().executePendingBindings();
    }

    马上绑定

    当变量变化时。绑定在下一帧到来前也要随之改变。

    然而。当绑定须要马上运行时,能够调用强制运行方法executePendingBindings()

    后台线程

    能够在后台线程中改变数据模型,仅仅要它不是集合。

    数据绑定将每一个变量或字段保存到本地,以避免不论什么并发问题。

    属性Setters

    仅仅要绑定值有变化,生成的绑定类就会在视图中调用setter方法。数据绑定框架能够自己定义要调用的方法来设置值。

    自己主动Setters

    对一个属性来说,数据绑定试图查找setAttribute方法。这与属性的命名空间没有关系。仅仅与属性本身有关。

    比如 TextView 的属性 android:text 上的表达式。数据绑定将查找setText(String) 方法,假设表达式返回值为 int,则会调用 setText(int)方法。注意表达式要返回正确的类型。有必要则使用cast进行类型转换。即使没有给定的属性,数据绑定也会运行。能够通过数据绑定调用setter方法创建属性。

    比如。DrawerLayout没有不论什么属性,但有非常多setter方法,能够调用setter方法自己主动创建属性。

    <android.support.v4.widget.DrawerLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:scrimColor="@{@color/scrim}"
        app:drawerListener="@{fragment.drawerListener}"/>

    重命名Setters

    对于属性setter方法与名字不匹配的情况,能够通过BindingMethods凝视关联名字和方法。

    类中包括BindingMethod凝视。当中能够重命名set方法。比如,android:tint 属性与setImageTintList(ColorStateList)方法相关。而与setTint不正确应。

    @BindingMethods({
           @BindingMethod(type = "android.widget.ImageView",
                          attribute = "android:tint",
                          method = "setImageTintList"),
    })
    开发人员不须要重命名setter方法,安卓框架已经做好了这方面的工作。

    自己定义 Setters

    一些属性须要自己定义绑定逻辑。android:paddingLeft 属性并没有相应的setter方法。可是存在setPadding(left, top, right, bottom)方法。通过 BindingAdapter 凝视来自己定义属性调用的静态setter方法。android 系统已经创建了 BindingAdapter 函数,以下是 paddingLeft 属性相应的函数:

    @BindingAdapter("android:paddingLeft")
    public static void setPaddingLeft(View view, int padding) {
       view.setPadding(padding,
                       view.getPaddingTop(),
                       view.getPaddingRight(),
                       view.getPaddingBottom());
    }
    绑定适配器对于其它自己定义类型非常有帮助。比如能够在其它线程中自己定义载入图片。

    假设绑定适配器有冲突,则开发人员自己定义的将会重写系统默认的绑定适配方法。一个适配器还能够有多个參数:

    @BindingAdapter({"bind:imageUrl", "bind:error"})
    public static void loadImage(ImageView view, String url, Drawable error) {
       Picasso.with(view.getContext()).load(url).error(error).into(view);
    }
    <ImageView app:imageUrl=“@{venue.imageUrl}”
    app:error=“@{@drawable/venueError}”/>
    假设用于 ImageView 的 imageUrl和 error 參数都存在而且 imageUrl 是 string 类型、error 是 drawable 类型 则就会调用上面定义的适配器。

    在匹配适配器的时候。 会忽略自己定义的命名空间你也能够为 android 命名空间的属性自己定义适配器。

    @BindingAdapter("android:paddingLeft")
    public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
       if (oldPadding != newPadding) {
           view.setPadding(newPadding,
                           view.getPaddingTop(),
                           view.getPaddingRight(),
                           view.getPaddingBottom());
       }
    }
    仅仅能使用一个抽象方法的接口或抽象类调用事件处理程序。

    @BindingAdapter("android:onLayoutChange")
    public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
           View.OnLayoutChangeListener newValue) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            if (oldValue != null) {
                view.removeOnLayoutChangeListener(oldValue);
            }
            if (newValue != null) {
                view.addOnLayoutChangeListener(newValue);
            }
        }
    }
    当一个监听器有多种方法时,必须要切割成多个监听器。

    比如。OnAttachStateChangeListener包括两个方法, onViewAttachedToWindow() 和onViewDetachedFromWindow()。必须创建两个接口用于区分属性和处理程序。

    @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
    public interface OnViewDetachedFromWindow {
        void onViewDetachedFromWindow(View v);
    }
    
    @TargetApi(VERSION_CODES.HONEYCOMB_MR1)
    public interface OnViewAttachedToWindow {
        void onViewAttachedToWindow(View v);
    }
    一个监听器的改变也会影响另外一个,所以必须有三个不同的绑定适配器,每一个监听器分别相应一个,两个监听器相应第三个绑定适配器。

    @BindingAdapter("android:onViewAttachedToWindow")
    public static void setListener(View view, OnViewAttachedToWindow attached) {
        setListener(view, null, attached);
    }
    @BindingAdapter("android:onViewDetachedFromWindow")
    public static void setListener(View view, OnViewDetachedFromWindow detached) {
        setListener(view, detached, null);
    }
    @BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
    public static void setListener(View view, final OnViewDetachedFromWindow detach,
            final OnViewAttachedToWindow attach) {
        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
            final OnAttachStateChangeListener newListener;
            if (detach == null && attach == null) {
                newListener = null;
            } else {
                newListener = new OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {
                        if (attach != null) {
                            attach.onViewAttachedToWindow(v);
                        }
                    }
                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        if (detach != null) {
                            detach.onViewDetachedFromWindow(v);
                        }
                    }
                };
            }
            final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
                    newListener, R.id.onAttachStateChangeListener);
            if (oldListener != null) {
                view.removeOnAttachStateChangeListener(oldListener);
            }
            if (newListener != null) {
                view.addOnAttachStateChangeListener(newListener);
            }
        }
    }
    上面的样例比正常的要略微复杂,因为视图对监听器用加入和删除方法替代set方法。

    android.databinding.adapters.ListenerUtil类帮助跟踪之前的监听器,以便在绑定适配器中及时移除相应的监听器。

    通过用@TargetApi(VERSION_CODES.HONEYCOMB_MR1)凝视接口OnViewDetachedFromWindow and OnViewAttachedToWindow 。数据绑定代码生成器知道仅仅能在产生蜂窝MR1和新设备中运行时才会生成监听器。

    转换器(Converters

    对象转换

    当绑定表达式返回一个对象时候。将会自己主动调用 set 函数、重命名的函数、或者自己定义的 setter 中的一个。表达式返回的对象将会转换为该函数的參数类型。

    使用 ObservableMaps 来保存数据会比較简单。

    比如:

    <TextView
       android:text='@{userMap["lastName"]}'
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    这里的 userMap 返回的对象将制动转换为 setText(CharSequence) 的參数。

     假设參数不明白,则开发人员须要强制转换为须要的类型。

    自己定义转换规则

    有时候參数应该能够自己主动转换,比如

    <View
       android:background="@{isError ? @color/red : @color/white}"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
    上面的背景须要一个 Drawable 对象,可是表达式的返回值为整数对象的颜色值。这样的情况下。颜色值须要转换为 ColorDrawable。 这样的转换通过一个静态函数完毕,该函数带有一个 BindingConversion 注解。

    @BindingConversion
    public static ColorDrawable convertColorToDrawable(int color) {
       return new ColorDrawable(color);
    }
    须要注意的是,转换是在 setter上完毕的,所以不能把不同的类型混合使用:

    <View
       android:background="@{isError ?

    @drawable/error : @color/white}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

    三、Android Studio 支持数据绑定

    Android Studio支持数据绑定表达式的语法高亮显示。并在编辑器中显示不论什么表达式语言语法错误。

    预览窗格显示数据绑定表达式的默认值。以下从布局XML文件的元素实例摘录。

    <TextView android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@{user.firstName, default=PLACEHOLDER}"/>
    假设项目的设计阶段须要显示一个默认值,能够使用工具属性而不是默认的表达式的值。

    到此Data Binding官方文档就翻译完了。

  • 相关阅读:
    [Ansible]copy 模块
    [Ansible]script模块
    [Ansible]command shell模块
    [Ansible]Systemd 模块
    [Ansible]YUM 模块
    [Ansible]yum_repository模块 添加 删除yum源
    [Ceph]osd 无法启动 start request repeated too quickly for ceph-osd@1.service
    [Ceph]pool 删除 Error EPERM: pool deletion is disabled; you must first set the mon_allow_pool_delete config option to true before you can destroy a pool
    题解 烷基计数 加强版 加强版
    Polya 定理 学习笔记
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8472420.html
Copyright © 2011-2022 走看看