3. Data Binding 中的布局文件详解

3.1. import

3.1.1 data标签内可以有0个或者多个 import 标签。这样你可以在布局文件中像在 Java 代码中一样引用这些类

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

导入类以后,在databinding表达式中这个类就可以像在 java 代码中一样使用,比如用来定义变量,比如访问类中的静态方法和属性。例如,现在 View 可以在表达式中如下面这样引用:

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

3.1.2 当类名发生冲突时,还可以使用 alias 重命名:

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

现在,Vista 可以用来在布局文件中引用com.example.real.estate.View,同时 View 也能被使用,用来引用android.view.View

3.1.3 导入的类型可以用于变量的类型引用和表达式中:

<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 还没有对导入提供自动补全的支持,但你的应用仍然可以正常被编译。你也可以在变量定义中使用完整的包名来解决这个问题。

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

3.1.4 导入的类后,就可以在表达式中使用类的静态属性/方法:

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

3.1.5 和 Java 一样,java.lang.* 会被自动导入。

3.2. 变量

data 标签中可以有任意数量的 variable 标签。每个 variable 标签描述了会在 binding 表达式中使用的属性。

<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 接口的类或接口,变量的变动不会引起 UI 的变化!

对于不同配置有不同的布局文件时(比如横屏竖屏的布局),这些布局文件中定义的变量会被合并,所以这些不同配置的布局文件之间不能有冲突的变量定义。

自动生成的 binding 类会为每一个变量产生 getter/setter 函数。这些变量会使用 Java 的默认值,直到 setter 函数被调用。默认值有 null,0(int),false(boolean)等。

binding 类还会生一个命名为 context 的特殊变量,这个变量可以被用于 binding 表达式中。context 变量其实是 rootView 的 getContext() 的返回值。如果在布局文件中自己定义 context 变量,默认的 context 变量会被自己定义的显式 context 变量覆盖。

3.3. 自定义 Binding 类名

默认情况下,binding 类的名称取决于布局文件的命名,以大写字母开头,移除下划线,后续字母大写并追加 “Binding” 结尾。这个类会被放置在 databinding 包中。举个例子,布局文件 contact_item.xml 会生成 ContactItemBinding 类。如果 module 包名为 com.example.my.app,binding 类会被放在 com.example.my.app.databinding 中。

3.3.1 通过设置 data 标签中的 class 属性,可以修改 Binding 类的命名与位置。比如:

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

会在 databinding 包中生成名为 ContactItem 的 binding 类。

3.3.2 如果需要放置在应用程序的包名下,可以在前面加 “.”,比如:

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

ContactItem 会直接生成在 module 包下。

3.3.3 如果提供完整的包名,binding 类可以放置在任何包名中,比如:

<data class="com.example.ContactItem">
    ...
</data>

3.4. Includes

通过使用应用程序命名空间+变量名命名的属性,布局中的变量可以从包含include标签的布局中传递到 include 的子布局中使用。比如:

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

xmlns:bind="http://schemas.android.com/apk/res-auto" bind 是应用程序命名空间,bind:user user 是变量名。另外需要注意,name.xml 与 contact.xml 中也都需要声明 user 变量。比如 name.xml:

<?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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="@android:color/holo_red_dark"
            android:text="@{`include : ` + user.firstName}"/>
    </LinearLayout>

</layout>

上面布局中如果 user.firstName 值为 "zhao",那么 TextView 中就会显示include:zhao

Data binding 不支持 merge 直接包含 include 节点。比如下面代码就不能正常运行:

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

3.5. DataBinding表达式语言

3.5.1 通用特性

bingding 表达式语言与 Java 表达式有很多相似之处。下面是相同之处:

  • 数学运算 + - / * %
  • 字符串连接 +
  • 逻辑运算 && ||
  • 二进制运算 & | ^
  • 一元运算符 + - ! ~
  • 位移运算 >> >>> <<
  • 比较运算 == > < >= <=
  • instanceof
  • 组 ()
  • 字面量 - 字符,字符串,数字,null
  • 类型转换
  • 函数调用
  • 字段存取
  • 数组存取 []
  • 三元运算符 ?:

例如:

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

3.5.2 缺失的操作符

一些 Java 中的操作符在表达式语法中不能使用

  • this
  • super
  • new
  • 显式泛型调用 <T>

3.5.3 Null合并运算符

Null合并运算符(??)会在非 null 的时候选择左边的操作,反之选择右边。

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

等同于

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

3.5.4 属性引用

正如上面“编写 data binding 表达式”中所讨论的 JavaBean 的引用。在 DataBinding 表达式引用了一个对象的属性时,它和访问字段,getter,或者 ObservableFields使用相同的格式。

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

3.5.5 避免NullPointerException

自动生成的 data binding 代码会自动检查和避免 null pointer exceptions。举个例子,在表达式 @{user.name} 中,如果 user 是 null,user.name 会赋予默认值 null。如果你引用了 user.age,因为 age 是 int 类型,所以默认赋值为 0。

3.5.6 容器类

通用的容器类:数组,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<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]}"

3.5.7 字符串字面量

当使用单引号包裹属性时,就可以很简单地在表达式中使用双引号:

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

当使用双引号包裹属性时,字符串字面量就可以用双引号转义符号"或者单引号'或者反引号(`) 来包裹

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

3.5.8 资源

可以在 DataBinding 表达式中使用普通的语法来引用资源:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

字符串格式化和复数形式字符串可以按下面这样使用:

string.xml

<string name="firstname">FirstName: %1$s</string>
<string name="lastname">LastName: %1$s</string>
<plurals name="banana">
    <item quantity="zero">zero</item>
    <item quantity="one">one</item>
    <item quantity="other">other</item>
</plurals>

layout.xml

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

当复数形式并且有多个参数时,这样使用:

strings.xml

<plurals name="numbers">
    <item quantity="one">Have an number</item>
    <item quantity="other">Have %1$d numbers</item>
</plurals>

layout.xml

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

一些资源需要显示类型调用

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

results matching ""

    No results matching ""