Jetpack学习3--使用可观察的对象&生成绑定类

[TOC]

Jetpack学习3--使用可观察的对象&生成绑定类

使用可观察的对象

可观察性是指对象通知其他人数据变化的能力。数据绑定库允许您使对象,字段或集合可观察。

任何普通的旧对象都可以用于数据绑定,但是修改对象不会自动导致UI更新。数据绑定可用于使数据对象能够在数据发生更改时通知其他对象,即侦听器。有三种不同类型的可观察类:objects, fields, and collections.

当其中一个可观察数据对象绑定到UI并且数据对象的属性发生更改时,UI将自动更新。

可观察的字段

创建实现Observable接口的类涉及到一些工作,如果类只有几个属性,那么这些工作就不值得了。在这种情况下,您可以使用泛型Observable类和以下原始特定类来使字段可观察:

可观察字段是具有单个字段的自包含可观察对象。原始版本在访问操作期间避免装箱和解箱。要使用这种机制,请在Java编程语言中创建一个public final属性,或者在Kotlin中创建一个只读属性,如下面的示例所示:

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

要访问字段值,使用set()和get()访问器方法,如下所示:

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

注意:Android Studio 3.1及更高版本允许您使用LiveData对象替换可观察字段,这为您的应用提供了额外的好处。有关更多信息,请参阅使用LiveData通知UI有关数据更改的信息

可观察的集合

一些应用程序使用动态结构来保存数据。可观察集合允许使用密钥访问这些结构。如果键是引用类型,比如字符串,ObservableArrayMap类非常有用,如下面的例子所示:

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

在布局中,可以使用key使用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"/>

当key是int时可以使用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"/>

可观察对象

实现Observable接口的类可以注册监听器,当可观察对象属性改变时可以通知它。

Observable接口具有添加和删除侦听器的机制,但是您必须决定何时发送通知。为了简化开发,数据绑定库提供了BaseObservable类,该类实现侦听器注册机制。实现BaseObservable的数据类负责在属性发生变化时发出通知。这是通过为getter分配一个Bindable注解,并在setter中调用notifyPropertyChanged()方法来实现的,如下面的示例所示:

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的类,该类包含用于数据绑定的资源的id。Bindable注解在编译期间在BR类文件中生成一个条目。如果不能更改数据类的基类,则可以使用PropertyChangeRegistry对象实现Observable接口,以有效地注册和通知侦听器。

生成绑定类

数据绑定库生成用于访问布局的变量和视图的绑定类。此页面显示如何创建和自定义生成的绑定类。

生成的绑定类将布局变量与布局中的视图链接起来。绑定类的名称和包可以customized。所有生成的绑定类都继承自ViewDataBinding类。

为每个布局文件生成一个绑定类。默认情况下,类的名称基于布局文件的名称,将其转换为Pascal大小写并向其添加Binding后缀。上面的布局文件名是activity_main.xml,因此相应生成的类是ActivityMainBinding。该类保存布局属性(例如,user变量)到布局视图的所有绑定,并且知道如何为绑定表达式赋值。

创建绑定对象

在对布局进行inflating之后,应该很快创建绑定对象,以确保在使用布局中的表达式绑定到视图之前不会修改视图层次结构。将对象绑定到布局的最常用方法是使用绑定类上的静态方法。您可以通过使用inflate()绑定类的方法来扩展视图层次结构并将对象绑定到该层次结构,如以下示例所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    MyLayoutBinding binding = MyLayoutBinding.inflate(getLayoutInflater());
}

除了LayoutInflater对象之外,inflate()方法还有另一个版本,它接受ViewGroup对象,如下面的示例所示:

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

如果使用不同的机制对布局进行inflate,则可以将其单独绑定,如下所示:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有时无法预先知道绑定类型。在这种情况下,可以使用DataBindingUtil类创建绑定,如下面的代码片段所示:

View viewRoot = LayoutInflater.from(this).inflate(layoutId, parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bind(viewRoot);

如果您在 Fragment, ListView, or RecyclerView adapter,中使用数据绑定,您可能更喜欢使用bindings类或DataBindingUtil类的inflate()方法,如下面的代码示例所示:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

带ID的视图

数据绑定库在绑定类中为布局中具有ID的每个视图创建一个不可变字段。例如,数据绑定库从以下布局中创建TextView类型的firstName和lastName字段:

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

该库一次性从视图层次结构中提取包括id在内的视图。这种机制比为布局中的每个视图调用findViewById()方法要快。

ID不是数据绑定的必要条件,但是扔有些情况需要从代码中访问视图。

变量

数据绑定库为布局中声明的每个变量生成访问器方法。例如,下面的布局在绑定类中为user,image, andnote变量生成setter和getter方法:

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

ViewStubs

​ 与普通视图不同,ViewStub对象一开始是一个不可见的视图。当它们变得可见或被明确告知要inflate时,它们会通过inflate另一个布局来替换布局中的自己。

​ 因为ViewStub基本上从视图层次结构中消失,所以绑定对象中的视图也必须消失,以便垃圾收集能够会是它。因为视图是最终的,所以一个ViewStubProxy对象在生成的绑定类中代替了ViewStub,当ViewStub存在时,您可以访问它,当ViewStub inflated时,您还可以访问inflated视图层次结构。

​ 在inflating另一个布局时,必须为新布局建立绑定。因此,ViewStubProxy必须监听ViewStub``OnInflateListener并在需要时建立绑定。由于在给定的时间内只能存在一个侦听器,所以ViewStubProxy允许您设置一个OnInflateListener,它在建立绑定之后调用这个OnInflateListener

立即绑定

当一个变量或可观察对象发生变化时,数据绑定库计划在下一帧之前执行绑定。然而,有时必须立即执行绑定。要强制执行,请使用executePendingBindings()方法。

高级绑定

动态变量

有时,特定的绑定类是未知的。例如,RecyclerView.Adapter针对任意布局的操作不知道特定的绑定类。它仍然必须在调用onBindViewHolder()方法期间分配绑定值。

在以下示例中,RecyclerView绑定的所有布局都具有 item变量。该BindingHolder对象有一个getBinding()返回ViewDataBinding基类的方法 。

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

注意:数据绑定库在模块包中生成一个名为BR的类,在上面的示例中,库自动生成BR.item变量。

后台线程

您可以在后台线程中更改除了集合以外的数据模型。数据绑定会在计算期间隔离每一个变量/字段以避免任何并发问题

自定义绑定类名称

默认情况下,将根据布局文件的名称生成绑定类,以大写字母开头,删除下划线(_),并大写后面一个字母,且添加单词Binding作为后缀。该类放在 databinding模块包下的包中。例如,布局文件 contact_item.xml生成ContactItemBinding类。如果模块包是com.example.my.app,则绑定类放在 com.example.my.app.databinding包中。

通过调整data元素中的class属性,可以重命名绑定类或将绑定类放在不同的包中 。例如,下面的布局在当前模块的databinding包中生成ContactItem绑定类:

<data class="ContactItem">
    …
</data>

您可以通过在类名前加一个句点在不同的包生成绑定类。以下示例在模块包中生成绑定类:

<data class=".ContactItem">
    …
</data>

你可以使用完整包名在你想要的包中生成绑定类。以下示例ContactItemcom.example包中创建绑定类 :

<data class="com.example.ContactItem">
    …
</data>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容