美文网首页
(三、3)布局和绑定表达式

(三、3)布局和绑定表达式

作者: 鹿小纯0831 | 来源:发表于2018-10-22 10:30 被阅读19次

表达式语言允许您编写处理视图调度的事件的表达式。 数据绑定库自动生成将布局中的视图与数据对象绑定所需的类。

数据绑定布局文件略有不同,以布局的根标签开头,后跟数据元素和视图根元素。 此视图元素是您的根在非绑定布局文件中的位置。 以下代码显示了一个示例布局文件:

<?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变量描述了可在此布局中使用的属性。

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

布局中的表达式使用“@ {}”语法写入属性属性中。 这里,TextView文本设置为用户变量的firstName属性:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}" />

注意:布局表达式应保持小而简单,因为它们不能进行单元测试并且IDE支持有限。 为了简化布局表达式,您可以使用自定义绑定适配器。

一、数据对象

我们现在假设您有一个普通的对象来描述User实体:


public class User {
  public final String firstName;
  public final String lastName;
  public User(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
}

这种类型的对象具有永不改变的数据。 在应用程序中,通常会读取一次并且之后不会更改的数据。 也可以使用遵循一组约定的对象,例如Java中的访问器方法的使用,如以下示例所示:

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

从数据绑定的角度来看,这两个类是等价的。 用于android:text属性的表达式@ {user.firstName}访问前一类中的firstName字段和后一类中的getFirstName()方法。 或者,如果该方法存在,它也会解析为firstName()。

二、绑定数据

为每个布局文件生成绑定类。 默认情况下,类的名称基于布局文件的名称,将其转换为Pascal大小写并向其添加Binding后缀。 上面的布局文件名是activity_main.xml,因此相应的生成类是ActivityMainBinding。 此类包含布局属性(例如,用户变量)到布局视图的所有绑定,并知道如何为绑定表达式指定值。创建绑定的推荐方法是在扩展布局时执行此操作,如 如下例所示:

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

在运行时,应用程序在UI中显示Test用户。 或者,您可以使用LayoutInflater获取视图,如以下示例所示:

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

如果在Fragment,ListView或RecyclerView适配器中使用数据绑定项,则可能更喜欢使用绑定类或DataBindingUtil类的inflate()方法,如以下代码示例所示:

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

三、表达语言

共同特征

表达式语言看起来很像托管代码中的表达式。 您可以在表达式语言中使用运算符和关键字:
例子:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
缺少操作

您可以在托管代码中使用的表达式语法中缺少以下操作:

  • this
  • super
  • new
  • 显式通用调用
空结合运算符

空合并运算符(??)选择左操作数(如果它不为空)或右(如果前者为空)。

android:text="@{user.displayName ?? user.lastName}"

这在功能上等同于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"
属性参考

表达式可以使用以下格式引用类中的属性,这对于fields,getters和ObservableField对象是相同的:

android:text="@{user.lastName}"
避免空指针异常

生成的数据绑定代码会自动检查空值并避免空指针异常。 例如,在表达式@ {user.name}中,如果user为null,则为user.name分配其默认值null。 如果引用user.age,其中age的类型为int,则数据绑定使用默认值0。

集合

为方便起见,可以使用[ ]运算符访问公共集合,例如数组,列表,稀疏列表和映射。

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

注意:您还可以使用object.key表示法引用地图中的值。 例如,上面示例中的@ {map [key]}可以替换为@ {map.key}。

字符串文字

您可以使用单引号括起属性值,这允许您在表达式中使用双引号,如以下示例所示:

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

也可以使用双引号来包围属性值。 这样做时,字符串文字应该用后引号`包围:

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

您可以使用以下语法访问表达式中的资源:

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

某些资源需要显式类型评估,如下表所示:
略。

四、事件处理

数据绑定允许您编写从视图调度的表达式处理事件(例如,onClick()方法)。 事件属性名称由侦听器方法的名称确定,但有一些例外。 例如,View.OnClickListener有一个onClick()方法,因此该事件的属性是android:onClick。
click事件有一些专门的事件处理程序需要除android:onClick以外的属性以避免冲突。 您可以使用以下属性来避免这些类型的冲突:

您可以使用以下机制来处理事件:

  • 方法参考:在表达式中,您可以引用符合侦听器方法签名的方法。 当表达式求值为方法引用时,Data绑定将方法引用和所有者对象包装在侦听器中,并在目标视图上设置该侦听器。 如果表达式求值为null,则数据绑定不会创建侦听器并改为设置空侦听器。
  • 侦听器绑定:这些是在事件发生时计算的lambda表达式。 数据绑定总是创建一个侦听器,它在视图上设置。 调度事件时,侦听器会计算lambda表达式。
方法参考

事件可以直接绑定到处理程序方法,类似于android:onClick的方式可以分配给活动中的方法。 与View onClick属性相比,一个主要优点是表达式在编译时处理,因此如果该方法不存在或其签名不正确,则会收到编译时错误。
方法引用和侦听器绑定之间的主要区别在于,实际的侦听器实现是在绑定数据时创建的,而不是在触发事件时创建的。 如果您希望在事件发生时评估表达式,则应使用侦听器绑定。
要将事件分配给其处理程序,请使用普通绑定表达式,其值为要调用的方法名称。 例如,请考虑以下示例布局数据对象:

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

绑定表达式可以将视图的单击侦听器分配给onClickFriend()方法,如下所示:

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

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

监听器绑定

侦听器绑定是在事件发生时运行的绑定表达式。 它们类似于方法引用,但它们允许您运行任意数据绑定表达式。 适用于Gradle版本2.0及更高版本的Android Gradle插件提供此功能。
在方法引用中,方法的参数必须与事件侦听器的参数匹配。 在侦听器绑定中,只有您的返回值必须与侦听器的预期返回值匹配(除非它期望无效)。 例如,考虑以下具有onSaveClick()方法的presenter类:

public class Presenter {
    public void onSaveClick(Task task){}
}

然后,您可以将click事件绑定到onSaveClick()方法,如下所示:

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

在表达式中使用回调时,数据绑定会自动创建必要的侦听器并为事件注册它。 当视图触发事件时,数据绑定会评估给定的表达式。 与常规绑定表达式一样,在评估这些侦听器表达式时,仍然可以获得数据绑定的null和线程安全性。
上面的示例中,我们尚未定义传递给onClick(View)的视图参数。 监听器绑定为监听器参数提供了两种选择:您可以忽略方法的所有参数,也可以命名所有参数。 如果您更喜欢为参数命名,可以在表达式中使用它们。 例如,上面的表达式可以写成如下:

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

如果您正在侦听的事件返回类型不为void的值,则表达式也必须返回相同类型的值。 例如,如果要监听长按事件,则表达式应返回布尔值。

public class Presenter {
    public boolean onLongClick(View view, Task task) { }
}
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果由于null对象而无法计算表达式,则数据绑定将返回该类型的默认值。 例如,引用类型为null,int为0,布尔值为false等。
如果需要使用带谓词的表达式(例如,三元),则可以使用void作为符号。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
避免复杂的听众

监听器表达式非常强大,可以使您的代码非常容易阅读。 另一方面,包含复杂表达式的侦听器使您的布局难以阅读和维护。 这些表达式应该像将UI中的可用数据传递给回调方法一样简单。 您应该在从侦听器表达式调用的回调方法中实现任何业务逻辑。

五、引用,变量和包含

数据绑定库提供导入,变量和包含等功能。 导入使布局文件中的类很容易引用。 变量允许您描述可用于绑定表达式的属性。 包括让您在整个应用中重复使用复杂的布局。

引用

导入允许您轻松引用布局文件中的类,就像在托管代码中一样。 可以在数据元素内使用零个或多个导入元素。 以下代码示例将View类导入布局文件:

<data>
    <import type="android.view.View"/>
</data>

导入View类允许您从绑定表达式中引用它。 以下示例显示如何引用View类的VISIBLE和GONE常量:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
输入别名

当存在类名冲突时,可以将其中一个类重命名为别名。 以下示例将com.example.real.estate包中的View类重命名为Vista:

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

您可以使用Vista来引用com.example.real.estate.View,View可以用来引用布局文件中的android.view.View。

导入其他类

导入的类型可以用作变量和表达式中的类型引用。 以下示例显示用作变量类型的User和List:

<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问题。

您还可以使用导入的类型来转换表达式的一部分。 以下示例将连接属性强制转换为User类型:

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

在表达式中引用静态字段和方法时,也可以使用导入的类型。 以下代码导入MyStringUtils类并引用其capitalize方法:

<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.lang。*会自动导入。

变量

您可以在数据元素中使用多个变量元素。 每个变量元素描述可以在布局上设置的属性,以在布局文件中的绑定表达式中使用。 以下示例声明用户,图像和注释变量:

<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接口的基类或接口,则不会观察变量。

当存在用于各种配置的不同布局文件(例如,横向或纵向)时,组合变量。 这些布局文件之间不得存在冲突的变量定义。

生成的绑定类对于每个描述的变量都有一个setter和getter。 变量采用默认的托管代码值,直到为引用类型调用setter-null,0表示int,false表示布尔值等。

根据需要生成名为context的特殊变量以用于绑定表达式。 context的值是来自根View的getContext()方法的Context对象。 上下文变量由具有该名称的显式变量声明覆盖。

包括

通过使用app命名空间和属性中的变量名,变量可以从包含的布局传递到包含的布局绑定中。 以下示例显示了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>
   <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>

数据绑定不支持include作为merge元素的直接子元素。 例如,不支持以下布局:

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

相关文章

  • (三、3)布局和绑定表达式

    表达式语言允许您编写处理视图调度的事件的表达式。 数据绑定库自动生成将布局中的视图与数据对象绑定所需的类。 数据绑...

  • Jetpack学习(二) 数据绑定

    一、配置 在应用的build.gradle里面加上 二、布局和绑定表达式 1、建立实体类User 2、xml布局文...

  • 美团面试总结

    1.angularJS双向绑定实现2.双向绑定的其他实现3.使用get,set实现双向数据绑定4.三列布局实现的三...

  • 2020-04-22v-bind:class,style

    1/三元表达式绑定 2、根据属性isActive决定是否有class =active: 3/绑定多个class 4...

  • DataBinding(二)变量及表达式

    1. 概述 在DataBinding的布局文件当中,可以引入各种变量,并在布局文件中使用变量表达式,达到数据绑定的...

  • Data Binding Library

    导航: 搭建环境 数据绑定编译器V2 数据绑定布局文件编写你的第一套数据绑定表达式数据对象绑定数据事件处理方法参考...

  • Databanding

    数据绑定布局 数据绑定布局文件以根标记layout开头,后跟data元素和view根元素。以下代码展示了示例布局文...

  • 小程序基础

    内容: 一:数据绑定 1.1 动态内容绑定:页面的.js文件中 数据绑定 1.2动态属性绑定 1.3三元表达式 1...

  • (三、5)生成的绑定类

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

  • Data Binding 详解(二)-布局和绑定表达式

    知是行之始,行是知之成。文章配套的 Demo:https://github.com/muyi-yang/DataB...

网友评论

      本文标题:(三、3)布局和绑定表达式

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