DataBinding 官方文档 翻译笔记

本文深入解析了Android DataBinding库的作用及其实现原理,介绍了如何在项目中配置和使用DataBinding,探讨了不同版本编译器的区别,并详细阐述了数据绑定的各种应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Data Binding Library

作用 : 在声明式布局的时候,最小化 页面布局和应用逻辑 之间的glue code (胶水代码)。

前提
1. Android 2.1(API level 7+)
2. Gradle 1.5.0- alpha 1

环境搭建

  1. 确保已经安装了绑定库
  2. 配置gradle脚本
  3. 开发工具 android studio 1.3 …
android {
    dataBinding{
        enabled = true
    }
}

注意: 如果应用程序模块中使用了数据绑定库,则APP也需要配置gradle脚本

Data Binding Compiler V2

在 Android gradle 插件 3.1.0 Cannary 6 中 附带了 可选的新 编译器。如果想使用新的DataBinding 编译器 ,可以在 gradle.properties 文件中添加 android.databinding.enableV2=true

In compiler V2:
- VIewBinding 系列的类 将在java compiler 编译之前 由 Android Gradle Plugin 生成。这样可以避免 因为不相关的原因导致 java compiler 失败 ,从而导致得到太多误报错误。
- 在V1中 ,binding 系列的类将会在app编译完成后再次生成(去分享生成的代码并关联到 常量‘BR’ 和 ‘R’ 文件 )。在V2 中,绑定库为多模块项目 将 保留 生成的binding 系列类和映射信息 从而 显著提高 数据绑定性能。

注意: 新的V2编译器 是向后不兼容,所以使用v1编译的库不能 被V2使用,反之亦然。

V2 删除了一些 很少使用的功能 去允许如下更改:
* 在V1,一个应用程序可以在依赖中 提供 可以重写覆盖 原适配器的 binding adapters ,但在 V2 中 binding适配器 只能在 本身的模块/应用中和它的依赖中产生作用
* 先前,如果一个布局文件在两个或者多个不同的资源配置中 却被具有相同 id 但不同类的 VIew 所装载,DataBinding 将会找到最共同的父类。在V2,这将会默认配置给VIew 当类型在配置文件中不匹配。HeHe
* 在V2编译器下,不同模块不能再manifest清单中配置相同的包名,因为DataBinding 将会使用 包名来生成绑定映射器。

Data Binding Layout Files

数据绑定布局文件略有不同,它是以layout为root tag 紧随着 数据元素 data 和视图 根元素view root element。 视图根元素就是原来不适用数据绑定框架的布局文件。

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

布局文件中可以使用 包括数据描述属性 的 变量标签 。for example:

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

常用的表达式 方式 @{}

数据对象

数据对象可以是 普通 POJO类 和 JavaBeans类。
假设引用 @{viewModel.useName} 等价于 公有属性 userName 等价于 公有get方法 getUserName() 等价于 userName() 方法。

绑定数据

默认的 ,一个Bainding 类 会基于 布局文件名 生成,翻译布局文件名以 Pascal case (命名方法) 并且以”Binding” 结尾。
创建Binding的最简单方法是在inflating 过程中执行例如:

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

获取视图的方式

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

在listview 或者recycleView中使用DataBinding

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

事件处理

有两种方式去处理事件:
1. Method References:(方法引用): 开发者可以引用符合监听器方法签名的方法,当表达式被评估为方法引用的时候,数据绑定框架将会把方法引用和所有者对象包装在一个监听器中,并且将监听器赋给视图。如果表达式为空 ,将不会生成监听器。
2. 监听器binding ,用一些Lanbda表达式,原理与方法引用类似,当事件分发时,监听器将评估lambda表达式。Lambda ( 提取对应监听方法的入参 ) -> function(自定义传参 (可使用入参 变量 ))

避免复杂的监听器

原则是 是布局文件可阅读,可维护性更高。
存在一些特殊的点击事件,它们需要的别的属性 而不是 android:onClick 去避免冲突.
如下:
Class Listener Setter Attribute
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

布局细节

import 0..more

类似java中引用类。
类名冲突

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

导入的类型可以用作于变量和类型引用

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

Variables

变量类型将会在编译的时候被检查,如果类型体现了它们 实现了,Observable 或者observable.collection 接口,那么变量将可被观察。
生成的binding类将会有setter getter 方法对每一个被描述的变量。在setter方法被执行前,默认值将会是java 默认值,,引用对象 == null int == 0 Boolean == false
binding表达式将会生成一个名叫 context的变量,变量的值是从 View的 getContext() 方法中获取.

Custom Binding Class Names(自定义 绑定类 名称)

默认地,一个绑定类将会基于布局文件名 生成 例如 : contact_item.xml —> ContactItemBinding类,然后该类放在 databinding包下。
但是Binding类可以放在不同的包下可自定义命名

<data class="包名.类名">
</data>

Include

用于 布局文件的引用
不支持引用布局作为合并元素的直接子元素。

表达式语言

与java有共同特征的

Mathematical 数学运算符 + - / * % 
String 字符串拼接 concatenation +
Logical 逻辑运算 && ||
Binary 二进制运算 & | ^
Unary 一元运算符 + - ! ~
Shift 位运算 >> >>> <<
Comparison 比较运算符 == > < >= <=
instanceof 继承关系
Grouping () 
Literals - character, String, numeric, null
Cast
Method calls
Field access
Array access []
Ternary operator ?:

缺少的方法

java中可以用 lambda中无法使用

- this
- super
- new 
- Explicit generic invocation 显示泛型调用

空值 合并操作

空合并操作符 ?? 选择左边的值,如果左值不为空 否则 右值
android:text="@{user.displayName ?? user.lastName}"等价 android:text="@{user.displayName != null ? user.displayName : user.lastName}"

避免空指针异常

生成的数据绑定代码 将会自动检查空值和避免空指针异常。 当遇到空值 databinding将会使用各类型默认值代替

集合操作

公共集合包括 arrays lists sparse lists 和 map ,也许会通过操作符 [] 来方便使用
举例:

<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]}"

String Literals 字符串文字

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

Resources 资源文件

使用普通的表达式作为表达式的一部分是可以获取资源文件
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
格式化字符串 和 复数是可以通过 参数来评估的

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

一些资源需要明确的类型评估

Type Normal Reference Expression Reference
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

数据对象 Data Object

任何 POJO 都可以用于 DataBinding ,但是修改一个POJO 不会产生UI更新。Databinding的真正能力是给你的数据对象们通知的能力在器数据被修改时。 有3种 不同的 数据修改通知机制。Observable objectsobservable fieldsobservable collections

  • Observable Object
    实现Observable 接口的类 允许 绑定一个 附加的侦听器 到绑定的对象上,去监听所有的属性变化。
    Observable接口具有添加和删除侦听器的机制,但是通知是由开发者决定的。为了是开发更加容易,产生了一个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);
   }
}

Bindable 注解 在编译期间 在BR class 文件中生成一个条目。BR文件将会生成在Module 包下。
如果 数据类的基类不能被修改,通过使用简便的PropertyChangeRegistry实现的 Observable 接口更有效的存储和通知监听器。
- Observable fields
当创建观察量的工作量很小,开发者可能会为了缩小开发时间,而采用ObservableField方式和其同级对象 ObservableBoolean ObservableByte ObservableChar ObservableShort ObservableInt ObservableLong ObservableFloat ObservableDouble ObservableParcelable
可观察属性将会生成一个具有单个字段的可观察对象。原始版本将会在访问阶段 避免装箱和拆箱操作。为了使用将会在数据类中使用FInal field
for example:

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();
  • observable collection
    一些应用使用更加动态的结构去存储数据对象。可观察集合可以通过keys值来获取其数据对象
    ObservableArrayMap 是一个有效地方法当keys是索引对象 ,比如是 String
    code for example:
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

生成Binding Generated Binding

生成的Binding类 会根据xml 文件名 和配置生成 ,,继承自 ViewDataBinding。

Creating 生成过程

Binding的应该立马在Inflation 视图文件后生成 以确保 在Layout中使用表达式bingding View而不影响视图结构。
有几种方法去绑定 Layout,最常见的方法是使用 Binding Class的静态类方法。静态类方法的Inflate方法将会一次性 infaltes 视图结构 完成绑定。

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

If the layout was inflated using a different mechanism, it may be bound separately:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

Sometimes the binding cannot be known in advance. In such cases, the binding can be created using the DataBindingUtil class:

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

Views With IDs

如果布局中的View 包含Id 属性 ,则框架会为他们各自生成一个公有常量。Binding将会在视图上进行单个传递,通过ID获取视图。这种机制对一些视图将会比使用findVIewById更加高速。
for Example:

<TextView id = "firstName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
在bingding中 生成
   public final TextView firstName;
使用:
binding.firstName;

Variables

每一个变量 将会被给与一个访问方法

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

生成的访问方法:

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

Advanced Binding 高级Binding

Dynamic Variables

有时候 特定的binding类将不会被知道,比如在RecycleView.Adapter 操作针对任意布局将不会知道其对应的指定Binding Class。它必须在onBindViewHolder 中 分配Binding值。forExample
使用 BindingHolder 代替ViewHolder 使用期getBinding 方法 来回去 ViewDataBinding 对象。

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

Immediate Binding 立刻Binding

当变量 或者 可观察对象 改变时,,binding 将会计划改变 在下一帧的时候。有时候,必须立刻binding ,解决方法 是使用 executePendingBindings() 方法

Background Thread

牛逼的功能
You can change your data model in a background thread as long as it is not a collection. Data binding will localize each variable / field while evaluating to avoid any concurrency issues.

Attribute Setters

Automatic Setters 自动赋值

对于属性,data binding 尝试 去寻找属性的setAttribute 方法。相对于属性名称本身,属性的命名空间本部重要。
举一个例子: 在表达式中关联到 TextView的属性 android:text ,databinding 将会去寻找 setText方法。如果表达式返回的是一个 int ,databinding 将会去寻找 setText(int) 方法。请注意表达式返回的类型是否正确,必要时要进行 转换操作。注意: data Binding 将会依旧工作 即使 关联的View不存在 命名的属性。所以你可以简单=地生成一些属性 进行setter 通过 databinding 。举例:DrawerLayout 拥有大量没有xml 属性的setter 方法 :

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

Renamed Setters 重命名Setter

一些attributes有拥有setter方法但不符合命名规则,对于这些方法,这些属性可以通过 BindingMethods 注解来关联。这必须和一个类相关联并且包含BindingMethod 方法。举例 android:tint 其真正关联的方法是 setImageTintList(ColorStateList)方法 而不是 setTint.

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

Custom Setters 自定义Setter

终于到接触过的@BindingAdapter
Some attributes need custom binding logic. For example, there is no associated setter for the android:paddingLeft attribute. Instead, setPadding(left, top, right, bottom) exists. A static binding adapter method with the BindingAdapter annotation allows the developer to customize how a setter for an attribute is called.

The android attributes have already had BindingAdapters created. For example, here is the one for paddingLeft:

@BindingAdapter(“android:paddingLeft”)
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
Binding adapters are useful for other types of customization. For example, a custom loader can be called off-thread to load an image.

Developer-created binding adapters will override the data binding default adapters when there is a conflict.

You can also have adapters that receive multiple parameters.

@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);
}

Converters 转换器

## Object Conversions 对象转换器
当一个对象从表达式中返回,将会自动从renamed 和 传统setters 选择一个setter方法,该对象将会转换成所选setter所需要的参数类型。
有时 转换时自动进行的。比如 background方法:背景将会是一个Drawable ,但是给的Color 是一个Int 所以,会需要Int 转换成ColorDrawable

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

Android Studio Support for Data Binding

Android Studio supports many of the code editing features for data binding code. For example, it supports the following features for data binding expressions:

Syntax highlighting
Flagging of expression language syntax errors
XML code completion
References, including navigation (such as navigate to a declaration) and quick documentation
Note: Arrays and a generic type, such as the Observable class, might display errors when there are no errors.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值