Android DataBinding 详解

Android 安卓DataBinding(一)·基础 DataBinding 是谷歌官方发布的一个框架,其作用是实现数据绑定(Data binding),同时,也是因为有它,可以在安卓(Android)上实现MVVM架构。这样布局这边就算完事了,但是呢PeopleBean少了点什么呢,没错,就是他的信息,现在PeopleBean中所有的信息都还是null呢!但是,这样有时候避免不了import导入不同路径而相同的类名,此时呢,就要在import标签中用到alias属性了,就比如以下情况。在你的布局中找到最外层的布局,将光标放在如图位置。 阅读详情

       前几天小试牛刀写了一篇 Android DataBinding 初探,只是简单的介绍了一下 DataBinding 的几个小问题,并没有特别详细的去介绍 DataBinding 的更多方法,这几天看了一下 DataBinding 的官网的相关内容,觉得有必要把官网的用法记录一下,用来参考及以后使用时的参考,以前大家很多人都使用过注解框架,包括 Jake Wharton 大神的 ButterKnife,但 DataBinding 出来后,相信会对此类的框架形成碾压,毕竟是 Google 的官方出品,接下来我们切入正题了,开始详细的去介绍这个 Android DataBinding Library,先上一张概况图:


一、构建环境(Build Environment)

  • 要使用 DataBinding 数据库,先从 Android SDK Manager 的支持库里下载该库
  • 配置你的应用程序使用数据绑定,在应用程序模块,你的 build.gradle 文件添加数据绑定元素
  • 另外,需要注意你使用的 Android Studio 的兼容版本,需要 1.3 及以上的版本
使用下面代码段来配置数据绑定:
android {
    ....
    dataBinding {
        enabled = true
    }
}

二、数据绑定布局文件(Data Binding Layout Files)

1) DataBinding 表达式

数据绑定的布局文件和我们以前经常写的布局文件稍有不同,并从布局的根标记开始,后面依次是数据元素和视图根元素,即根布局是 layout,接下来是 data 节点,variable 节点,示例如下:

<?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>
数据中的用户变量描述此布局中可能使用的属性:

<variable name="user" type="com.example.User"/>
布局中的表达式使用 "@{}" 语法在属性中写入,在这里,TextView 的文本设置为用户 FirstName 属性:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}"/>
2)数据对象(Data Object)

假设你现在有一个普通的 Java 对象(User):

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;
   }
}
从数据绑定的角度来看,这两个类是等价的,用于 TextView 中的 android:text 属性的表达式 @{user.firstName} 将访问前者 User 对象中的 firstName 和后者 JavaBeans 对象中的 getFirstName 方法

3)数据绑定(Binding Data)

默认情况下,绑定类将根据 layout 文件的名称生成,首字母大写的命名规范,并添加 "Binding" 后缀,上述的布局文件是main_activity.xml,所以生成类是 MainActivityBinding, 该类将布局属性(例如 User 变量)的所有绑定保存到布局视图中,并知道如何为绑定表达式赋值,创建最简单的方法是在 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);
}
就这样,运行应用程序,你将会在 UI 中看到 Test User,或者你可以通过如下获取 View:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
如果你是在 ListView 或者 RecyclerView adapter 中使用 Data Binding 时,你可能使用:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
4)事件处理(Event Handing)

数据绑定允许你编写表达式处理,如 onClick 事件,有少数例外事件属性的名称受监听方法名称的约束,例如View.OnLongClickListener 有一个 onLongClick 方法,所以此事件的属性是:Android:onLongClick,它有两种处理事件的方法:

  • 方法引用:在表达式中,可以引用符合监听方法签名的方法,当表达式计算为方法引用时,数据绑定将监听器中的方法引用和所有者对象封装在一起,并在目标视图上设置监听器,如果表达式计算为 null,则数据绑定不会创建监听器,而是设置空监听器
  • 监听器绑定:这些是在事件发生时计算 Lambda 表达式,数据绑定总是创建一个监听器,它设置在视图上,当事件被发送时,监听器计算 Lambda 表达式

 方法引用:事件可以直接绑定到处理程序的方法,类似于 Android:onClick,相比来看 View#onClick 属性更重要的优势是,表达式在编译的时候处理的,所以如果方法不存在或签名是不正确的,你将会在编译时出错,方法引用和监听器绑定之间的主要区别是,当数据绑定时,实际的监听器实现将创建,而不是在触发时,如果你希望事件发生时对表达式进行审核,则应该使用监听器绑定,若将事件分配给其处理程序,请使用常规绑定表达式,该值是要调用的方法名,例如,如果你的数据对象有两种方法:

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}
绑定表达式可以为视图分配单击监听器:

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

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <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>


监听器绑定:监听器绑定是在事件发生运行时的绑定表达式,它类似于方法引用,但它容许你运行任意的数据绑定表达式,需要注意的是此功能在 Android Gradle2.0 及以上版本可用,在方法引用中,该方法的参数必须与事件监听器的参数相匹配,在监听器绑定中,只有返回值必须与监听器的期望返回值相匹配,示例如下:

public class Presenter {
    public void onSaveClick(Task task){}
}
然后你可以在你的 xml 文件中将点击事件绑定如下:

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

监听器由 Lambda 表达式表示,这些表达式只允许作为表达式的根元素,当表达中使用回调时,数据绑定会自动创建时间的必要监听器和寄存器,当视图触发事件时,数据绑定将审核给定的表达式,与常规绑定方式一样,在审核这些监听器表达式时,任然可以得到数据绑定的 null 和线程安全性

请注意,在上面的例子中,我们还没有定义 View 的参数,通过 onClick(Android.view.View)Listener 绑定为监听器参数提供两种选择:你可以忽略所有方法参数或命名所有参数,如果你更喜欢命名参数,则也可以在表达式中使用它们,例如,上面的表达式可以写成:

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)}" />
如果监听的事件返回类型无效的值,则表达式必须返回相同类型的值。例如,如果要监听长点击事件,则表达式应返回布尔值:

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}

android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
你还可以使用三元表达式:

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
避免复杂的监听:
监听器表达式是非常强大的,可以使你的代码很容易阅读。另一方面,含有复杂的表达式的监听让你的布局难以阅读和维护。这些表达式应该是简单的,从用户界面传递可用数据到回调方法。您可以在从监听器表达式调用的回调方法中实现任何业务逻辑


三、布局细节(Layout Details)

1)imports

可以在数据元素内使用零个或多个导入元素。这些可以参考在你的布局文件的类,就像在 java:

<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}"/>
当有类名冲突时,其中一个类可以重命名为“alias:”,如下:

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>
现在,Vista 可以用来参考的 com.example.real.estate.view 和视图可以用来参考 android.view.view 在布局文件中,导入类型可以用作变量和表达式中的类型引用:

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List<User>"/>
</data>
注意:Android Studio 还不能很好的兼容支持,变量可能不能在 IDE 中完成自动提示功能。但是你的应用程序将仍然可以编译,你可以通过使用完全限定名称来定义变量解决 IDE 的问题:

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
在表达式中引用静态字段和方法时也可以使用导入类型:

<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 语言,java * 自动导入

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

在编译时检查变量类型,因此,如果变量实现 Observable 或 Observable 的集合,则该类型应反映在类型中。如果变量是不执行 Observable* 接口的基类或接口,则不会观察变量,当有不同的布局文件的各种配置(如头像),变量将被合并。这些布局文件之间不能有冲突的变量定义生成一个名为上下文的特殊变量,用于在需要时绑定表达式。上下文的值是根目录的 getcontext(),上下文变量将被一个显式变量声明所覆盖

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

默认情况下,绑定类是基于布局的文件名生成,开始用大写,去掉下划线 "_",然后加后缀 "Binding",这个类将会被放置在一个绑定包的模块包下,例如,布局文件是 contant_item.xml 将生成 ContactItemBinding,如果模块封装为 com.example.my.app,那么它将被放置在 com.example.my.app.databinding

通过调整数据元素的类属性可以将绑定类重命名或放置在不同的包中。例如:

<data class="ContactItem">
    ...
</data>
这个生成绑定类中的模块封装在数据绑定包 ContactItem,如果类产生在不同的包中的模块封装内,它可能会加 ".",如下:

<data class=".ContactItem">
    ...
</data>
在这种情况下,ContactItem 直接在模块封装生成,如果提供完整的包,任何包可以使用:

<data class="com.example.ContactItem">
    ...
</data>
4)Includes

通过使用应用程序命名空间和属性中的变量名,可以将变量从包含布局中传递到包含布局中:

<?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>
在这里,必须有两 name.xml 和 contact.xml 布局文件的用户变量

数据绑定不支持包括合并元素的直接子项。例如,不支持下列布局:

<?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>
   <merge>
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>

5)表达式(Expression Language)

常用表达式给 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(三元运算) "?:"
示例如下:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

缺少的操作:
  • this
  • super
  • new
  • Explicit generic invocation
null 合并运算符:

null 合并运算符(??)左边不为 null 或正确,则返回左边,否则返回右边,如下:
android:text="@{user.displayName ?? user.lastName}"
在功能上和如下相同:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"

属性引用:

第一个前边已经提到了,DataBinding 表达式:JavaBean 引用的简短格式
当一个表达式引用一个类的属性,它仍使用同样的格式对于字段、getters 以及 ObservableFields
android:text="@{user.lastName}"
避免空指针(Avoiding NullPointerException)
Data Binding 代码生成时会自动检查是否为 null,来避免出现 null pointer exception 错误,例如在表达式 @{user.name} 中,如果 user 是 null,user.name 会赋予它的默认值 null,如果你引用 @{user.age},age 是 int 类型,那么它的默认值是 0

集合:

常见的集合:数组,列表,稀疏列表和 maps,可以访问使用 [ ] 运算符
<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]}"

字符串常量(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)}"

当复数需要多个参数时,所有参数都应通过:
  Have an orange
  Have %d oranges

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

一些资源需要显示类型判断:


四、数据对象(Data Objects)

任何普通的 java 对象(POJO)可用于数据绑定,但修改一个 POJO 不会造成UI更新。数据绑定的真正能力可以通过给数据对象在数据变化时通知来使用,有三种不同的数据更改通知机制,Observable 对象,ObservableFilelds 字段和 Observable Cllections 集合,当这些可观察数据对象绑定到 UI 和数据对象的属性更改时,用户界面将自动更新

Observable 对象(Observable Objects)
实现 Observable 接口类允许单个监听器连接到绑定对象以监听该对象所有属性的改变

Observable 接口有一个机制来添加和删除监听器,但通知是由开发人员管理,为使开发更容易,一个 BaseObservable 基类是为了实现监听器注册机制而创建。Data 类实现者仍然是负责通知时的性能变化。这是通过分配一个 Bindable 注释 getter 和 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 类的文件条目,BR 类文件会在模块包内生成,如果 Data 类的基类不能改变,Observable 接口通过 PropertyChangeRegistry 可以方便的来实现用于存储和有效的通知监听器

Observable 字段(ObservableFields)

一个小工作会涉及到创建 Observable 类,所以开发者们想节省时间或仅有几个特性可以用 observablefield 和它的同类observableboolean,observablebyte,observablechar,observableshort,observableint,observablelong,observablefloat,observabledouble,和observableparcelable。observablefields 完备可观察对象有一个单一的领域。原始版本避免装箱和拆箱过程中访问操作。若要使用,请在数据类中创建 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();

Observable 集合(Observable Clollections)

一些应用程序使用更多的动态结构来保存数据。Observable 集合允许对这些数据对象进行键控访问。ObservableArrayMap 用于键是引用类型,关键是引用类型,比如 String:
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
在布局中,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);
在布局中,可以通过索引访问 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"/>

五、Binding 生成(Generated Binding)

生成的 Banging 类将布局 variables 与布局内的 views 连接起来。如前所述,Binding 的名称和包可以自定义。生成的 Binding 类都扩展 ViewDataBinding

创建(Creating)

Binding 应在 inflation 后不久创建,以确保 View 层次结构在绑定到 Views 中的表达式之前不会受到干扰。有几个方法绑定到layout,最常见的是使用静态方法 Binding 类。inflation 方法载入 View 的层次结构并且绑定到它只需要这一步,结合这一步,还有一个更简单的版本,只需要一个 LayoutInflater,或一个 ViewGroup:
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
如果 layout 使用不同的机制 inflate,则可以单独绑定:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有时 Binding 不能事先知道,在这种情况下,可以使用 DataBindingUtil 类来创建 Binding:
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

ID 绑定(Views With IDs)

将在 layout 中使用 ID 生成每个 View 的公共 public final 字段,Binding 在 View 层次结构上执行单个传递,提取带 ID 的Views。这种机制可以比调用 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>
将生成一个 Binding 类:
public final TextView firstName;
public final TextView lastName;

IDS 几乎没有必要在 DataBinding,但仍然有一些情况下,访问 Views 仍然是必要的代码

Variables:

每个 vaiables 都会有给定的访问方法:
<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>
它会在Binding中结合生成setters和getters:
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 相比正常的 Views 略有不同,它们开始时是不可见的,当它们设置为可见或告知要载入时,它们通过载入另外一个layout 取代自己
由于 ViewStub 基本消失从View的层次结构,

因为 viewstub 基本消失从视图层次,在 Binding 对象的 View 也必须消失让收集,因为Views是最后一个 ViewStubProxy 对象以取代 ViewStub 的地方,给开发者获得 ViewStub,当 ViewStub 已经被载入时,它还可以存在并访问载入的 View 层结构

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

高级绑定(Advanced Binding)

动态变量:
有时,不知道具体的 Binding 类,例如,一个 RecyclerView 适配器对任意 layout 并不知道具体的 Binding 结合类,它任然必需在 onBindViewHolder 期间赋值给 Binding
在下面的例子中,该 RecyclerView 绑定的所有 layouts 有一个 "item"的Variable,该 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();
}

直接 Binding

当一个 Variable 或 Observable 变化时,binding 会在下一帧前被计划要求改变,但是有很多次在 Binding 时必需立即执行,要强制执行,使用 executePendingBindings() 方法

后台线程
只要它不是一个集合,你可以在后台中改变数据模型,在判断是否要避免任何并发问题时,Data Binding 会对每个 Variable/field本地化


六、属性 Setters

每当绑定值发生变化时,生成的绑定类必须使用绑定表达式在视图上调用 set 方法。Data Binding 框架具有自定义调用哪个方法来设置值的方法

自动安装(Automatic Setters)

对于一个属性,Data Binding 试图找到 setAttribute 方法,与该属性的 name space 并没有什么关系,仅仅与属性本身名称有关

例如,有关 TextView 的 android:text 属性的表达式会寻找一个 setText(String) 的方法,如果表达式返回一个 int,Data Binding会搜索的 setText(int) 方法,这里要注意:表达式要返回正确的类型,如果需要的话使用 casting,Data Binding 任然会工作,即使没有给定的名称属性存在,然后,你可以通过 Data Binding 轻松地为任何 setter "创造"属性,例如,DrawerLayout 没有任何属性,但大量的 setters,你可以使用自动 setters 来使用其中的一个:
<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

重命名 Setters(Renamed Setters)

一些有 Setters 的属性按名称并不匹配,对于这些方法,属性可以通过 BindingMethods 注解相关联,还必须与一个包含BindingMethod 注解类相关联,每一个用于重命名的方法,例如  andorid:tin 属性与  setImageTintList 相关联,而不与 setTint相关
@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})
上面的例子,开发者不太可能重命名编译程序,Android 框架属性已经实现了

自定义 Setters(Custom Setters)

有些属性需要自定义绑定逻辑,例如,对于 android:paddingLeft 属性并没有相关的setter,相反,setPadding (left、top、right、bottom)是存在,一个带有 BindingAdapter 注解的静态绑定适配器方法允许开发者自定义 setter 如何对于一个属性的调用
Android 的属性已经创造了 BindingAdapters,举例来说,对于 paddingLeft:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}
Binding 适配对其他定制类型非常有用,例如,自定义 loader 可以用异步载入图像
当有冲突时,开发人员创建的 Binding 适配器将覆盖 Data Binding 默认适配器
你也可以创建可以接收多个参数的适配器:
@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 时,该适配器被调用
  • 匹配的过程中自定义 name spaces 将被忽略
  • 你也可以为 Android name spaces 写适配器

绑定适配器方法可以在其处理程序中选择旧值。取旧值和新值的方法应具有所有属性的旧值,其次是新值:
@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);
        }
    }
}

当监听器有多个方法时,它必须被分割成多个监听器,例如,View.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);
        }
    }
}

上面的例子是比正常的稍微复杂,因为 View 使用添加和删除的监听者而不是为 View.OnAttachStateChangeListener 设置方法,android.databinding.adapters.listenerutil 类有助于保持跟踪,他们可能会在绑定适配器删除以前的 listener

七、转换(Converters)

对象转换(Object Conversions)

当从 Binding 表达式返回对象时,将从自动、重命名和自定义 setters 中选择一个配置器。对象将被转换为所选集的参数类型
这是为那些使用 ObservableMaps 保存方便,例如:
<TextView
   android:text='@{userMap["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
在 userMap 返回一个对象并且该对象将自动转换为 setText(CharSequence) 的参数类型,当有关参数类型可能混乱,开发人员需要在表达式中转换

自定义转换 Custom Conversions)

有时在特定类型之间应该自动转换。例如,当设置背景:
<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

这里,背景需要 Drawable 对象,但颜色是一个整数,不管何时有 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 对 DataBinding 的支持(Android Studio Support for Data Binding)

Android Studio 支持 DataBinding 代码的许多代码编辑功能,例如,它支持数据绑定表达式的下列功能:
  • 语法高亮
  • 表达式语言语法错误的标记
  • XML 代码完成
  • 引用和快速文档
注意:数组和泛型类型(如可观察类)可能在没有错误时显示错误

预览窗格显示数据绑定表达式的默认值,如果提供,以下从布局的 XML 文件的元素实例摘录,预览窗格中显示的默认文本值的占位符文本:
<TextView android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="@{user.firstName, default=PLACEHOLDER}"/>

如果你需要你的项目的设计阶段中显示一个默认值,你也可以使用工具的属性而不是默认的表达式的值,在设计 layout 描述属性

原文地址:点击打开链接

如有翻译错误,请指正




(二十九)安卓开发中DataBinding 和 ViewBinding详解 是 Android Jetpack 提供的一项功能,旨在替代传统的方法。它通过为每个 XML 布局文件生成一个绑定类,使得开发者可以直接通过这个类访问布局中的视图,而无需手动查找视图 ID。这不仅提高了代码的可读性,还提供了类型安全和空安全的好处。是一个更强大的工具,不仅提供 ViewBinding 的视图访问功能,还允许在 XML 布局中直接绑定数据源。它可以让 UI 组件与数据对象建立绑定关系,当数据发生变化时,UI 会自动更新,反之亦然(双向绑定)。 阅读详情

相关推荐

T型三电平关管顺序要求

摘要: T型三电平拓扑中,IGBT关断必须遵循“外管先关、内管后关”的时序,否则易导致器件损坏。正常导通时,外管与内管共同分担电压;正确关断时,外管先关使电流经内管续流至中点,避免电压过冲;若内管先关,电流路径被强制切断,杂散电感引发高压尖峰击穿器件。这一时序本质是为电流提供低应力续流路径,需通过硬件或软件严格保障。

yun0804的博客 2381

【Android】DataBinding 最全使用解析

【Android】DataBinding 最全使用解析

浪漫之圣 1万+

状态错误CS0234命名空间“Windows”中不存在类型或命名空间名“Management”(是否缺少程序集引用?)

解决源码编译UE打包失败问题

五百零一 2338

Android DataBinding 从入门到进阶,看这一篇就够

前言 DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常 启用 Dat

Eqiqi的博客 6365

常用的串口通信工具MobaXterm、Bus Hound、SScom等

本文介绍了四种PC端串口通信工具的功能和使用方法:MobaXterm支持SSH、FTP、串口等多种协议;Bus Hound用于监控CAN和串口数据;SScom专为串口和网口通信设计;ScriptCommunicator可进行串口通信并绘制波形图。每种工具都有详细的操作步骤图示,包括参数设置、数据监控等关键功能,并提供了官方和个人下载地址。这些工具各有特点,适用于不同的串口通信和数据分析场景。

sanqima的专栏 2494

Dynamics 365从入门到精通:一站式掌握微软智能商业应用平台

很多新手学Dynamics 365时,容易陷入“死记功能”的误区,其实这款平台的核心价值不是“功能多全”,而是“能适配企业业务流程,通过生态集成提升效率”。先从具体业务场景入手,理解每个功能的实际价值;再通过生态集成拓展系统能力;最后通过开发实现定制化,满足企业的特殊需求。按照这个思路,新手不用害怕复杂,一步步落地实战,3个月就能从“入门”到“能独立负责项目”。如果在学习过程中遇到具体问题(比如部署报错、数据迁移失败),欢迎在评论区留言,我会结合实战经验帮你解答!

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1041

Android DataBinding的基本使用

DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常 虽然dataBi

yzwfeng的专栏 9245

Temporal Fusion Transformer(TFT)与扩散模型融合:时间序列预测新范式

本文探讨了Temporal Fusion Transformer(TFT)与扩散模型融合的时间序列预测新范式。该融合方案利用TFT强大的特征提取与可解释性,为扩散模型提供精准的“导航图”,从而在噪声数据中实现鲁棒预测,并原生支持不确定性量化,输出完整的概率分布与置信区间,为金融、能源等领域的风险决策提供更可靠的依据。

weixin_29096377的博客 291

Android 安卓DataBinding详解(超详细)

由于这篇文章写来来会很长很长,为避免文章内容混乱,便于理清思路考虑,所以列出了目录。 Android 安卓DataBinding(时刻更新、维护一下文章) Android 安卓DataBinding(一)·基础 Android 安卓DataBinding(二)·入门 Android 安卓DataBinding(三)·单向绑定 BaseObservable Android 安卓DataBindin...

两行代码 踏踏实实de工作 8465

龙芯3A5000开发板PMON升级UEFI固件实战指南

固件是计算机硬件与操作系统之间的关键桥梁,负责初始化硬件并引导系统启动。其工作原理是在上电后执行自检、加载引导程序,最终将控制权移交操作系统。在国产化平台中,采用标准化的UEFI固件具有重要技术价值,它能提供图形化配置界面、统一硬件驱动模型,并支持安全启动等现代特性,从而显著提升硬件兼容性与系统管理效率。这一技术广泛应用于PC、服务器及嵌入式设备,尤其在需要运行银河麒麟等主流操作系统的龙芯开发板场景中,将传统PMON固件升级至UEFI,能实现更流畅的系统安装与更稳定的运行体验。本文基于固件升级与UEFI引导

weixin_42664597的博客 563

人工智能训练师三级考试大纲深度解读|理论+实操分值分布+六大模块占比

三级人工智能训练师考试分为理论和实操两大模块(各100分,合格线60分)。理论考试包含AI基础(30%)、数据标注(25%)、模型训练(20%)、测试(15%)和系统运维(10%),题型为客观题;实操考核侧重数据标注(40%)、模型测试(35%)和系统运维(25%),采用现场操作形式。复习应优先关注AI基础和数据标注等高分值模块,实操需重点掌握标准流程和结果准确性(占评分70%)。考试要求两模块均达标,理论侧重概念理解,实操强调规范执行与文档完整。

白话机器学习 1097

Android Databinding 详解

Android Databinding 详解 ​ DataBinding 是Google官方发布的一个框架,用来进行数据绑定。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity / Fragment 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。 ​ 使用DataBinding 的方法是在对应的 Module 的 build.gradle 文件里加入如下代码,同步之后就可以引

lixiong0713的博客 2万+

自然语言处理NLP星空智能对话机器人系列:Gavin大咖 Transformer 101问答集 第一问

自然语言处理NLP星空智能对话机器人系列:Transformer 101问答 [让天下没有难学的Transformer] 你问我答系列之一 Gavin大咖第一问: Transformer中的可训练Queries、Keys和Values矩阵从哪儿来? 以上是智华解答,供同学们参考。 星空智能对话机器人系列博客 NLP星空智能对话机器人系列:第二次星空智能对话机器人Zoom线上演示安排 NLP星空智能对话机器人系列:StarSpace: Embed All The Things NLP星空智能对

大模型与Agent智能体 991

Android之DataBinding详解

Data Binding 利弊 优势 DataBinding 出现以前,我们在实现 UI 界面时,不可避免的编写大量的毫无营养的代码:比如 View.findViewById();比如各种更新 View 属性的 setter:setText(),setVisibility(),setEnabled() 或者 setOnClickListener() 等等。 这些“垃圾代码”数量越多,越容易滋...

LVXIANGAN的专栏 4268

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Android 中的 DataBinding 详解

DataBinding 是 Android 开发中提升效率和代码质量的重要工具,其核心价值在于通过声明式绑定实现视图与数据的解耦。结合 ViewModel 和 LiveData,DataBinding 能完美支持 MVVM 架构,帮助开发者构建可维护、高性能的应用。尽管存在一些性能和调试上的挑战,但通过合理的代码设计和最佳实践,这些问题均可有效规避。建议在新项目中优先采用 DataBinding,并逐步在现有项目中进行迁移,以充分享受其带来的开发红利。

ZLGSPACE的博客 2236

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Android Jetpack组件之DataBinding详解

原文首发于微信公众号:躬行之(jzman-blog) 前面总结了 ViewModel、LiveData 及 Lifecycle 架构组件的使用,可先阅读下面文章详细了解: Android Jetpack组件之Lifecycle篇 Android Jetpack组件之LiveData详解 Android Jetpack组件之ViewModel篇 本篇主要侧重 dataBinding 的基本使用,主要内容如下: dataBinding支持 布局文件配置 绑定数据 特殊表达式 事件绑定 自定义绑定类 O.

躬行之 1041

android dataBinding详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 官方介绍地址:http://developer.Android.com/intl/zh-cn/tools/data-binding/guide.html 2015 Google IO 大会带来的 Data Binding 库使得 Android 开发者可以方便的实现 MVVM 架构模式。使用DataBinding可以改善应用程序的

阳光灿烂的日子的博客 751

Android DataBinding使用详解

文章目录1 基础入门2 单向数据绑定2.1 BaseObservable3 双向数据绑定4 事件绑定5 使用类方法8 BindingAdapter 参考: https://developer.android.google.cn/topic/libraries/data-binding/start.html https://www.jianshu.com/p/bd9016418af2 DataBin...

hongxue8888的博客 1087

Android DataBinding开始使用以及布局详解

了解如何为您的开发环境支持使用DataBinding,包括在Android Studio中支持数据绑定代码。 DataBinding提供了灵活性和广泛的兼容性-它是一个支持库,因此您可以将其用于运行Android 4.0(API 14级)或更高版本的设备。

yihanss的博客 983

Android DataBinding双向绑定详解:TwoWaySample示例教程

Android DataBinding双向绑定是一种强大的数据同步技术,它能够自动实现UI与数据模型之间的双向通信,极大简化了Android应用开发中的数据更新逻辑。本文将通过TwoWaySample示例项目,详细介绍Android DataBinding双向绑定的核心概念、实现方式和实际应用场景,帮助开发者快速掌握这一高效开发技术。 ## 双向绑定基础:从单向到双向的进化 在Android开

gitblog_00476的博客 418

Android DataBinding 使用的正确姿势详解

Android DataBinding 报错 Android DataBinding 详解也是错的; 昨天折腾了一下午照着官方写AndroidDataBinding 老是各种报错,最后这里贴出2018.10.13使用DataBinding 的正确使用: 转载注明出处,Write By xyx In csdn @包罗万码 地址:https://blog.youkuaiyun.com/a316375/...

a316375的博客 1987

Android DataBinding使用详解(二)

《Android DataBinding使用详解(一)》官方文档Demo下载地址1.在RecyclerView中使用DataBindingAdapter的定义方式和普通方式相同,都是继承了RecyclerView.Adapter,然后实现其中的方法,但是具体的实现方式还是有所差别的:public class RecyclerViewAdapter extends RecyclerView.Adapt

YangLe 1837
上一篇: Android Activity 完全解析(中)
下一篇: Android Glide 图片加载框架解析
QDJdeveloper
博客等级 码龄11年 108粉丝 49原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值