Data Binding 系列(三)布局和绑定表达式

栏目: IOS · Android · 发布时间: 5年前

内容简介:这种表达式语言(使用在

这种表达式语言( expression language )使我们可以使用表达式处理 view 的事件。 Data Binding 库会自动生成绑定类( binding class )用来处理 view 和 data 的绑定关系。

使用 Data Binding 的布局文件和传统的布局文件稍有不同,它的根标签是 layout ,里面会有一个 data 子标签和一个根 view 子标签。这个根 view 子标签和传统的布局文件是一样的。具体如下所示:

<?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>
复制代码

data 标签中声明的 user 变量,将会在绑定表达式中用到。

<variable name="user" type="com.example.User" />
复制代码

绑定表达式用于为属性赋值,它使用的语法是 @{} 。在下面的例子中, TextView 控件的属性 text ,被赋值为 user 变量的 firstName 属性值:

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

数据源

假设现在有一个描述 User 实体的数据对象:

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

这个数据类的成员属性都是不可变的、是 public 的。它还有另外一种写法,成员属性是 private 的,并且提供访问它们的方法:

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() 方法存在,也会调用这个方法。

可绑定的数据( Binding data

每个布局文件都会生成一个对应的绑定类。默认的绑定类的名字是文件名转为驼峰写法并加上后缀 Binding 。比如文件名是 activity_main.xml ,对应的绑定类名为 ActivityMainBinding 。这个绑定类保存了数据变量和 view 属性的绑定关系,并且知道如何为 view 属性赋值。推荐的方式是在加载布局的时候创建绑定类,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    binding.user = User("Test", "User")
}
复制代码

另外,还可以使用 inflate() 方法创建绑定类:

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
复制代码

如果是在 FragmentListViewRecyclerView 中使用 Data Binding ,你可能更倾向于使用 inflate() 方法或 DataBindingUtil 类,如下所示:

val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
复制代码

表达式语言( expression language

1 常用特性

表达式语言看起来很像代码里面的表达式。你可以在表达式中使用如下的操作符和关键字:

  • 计算相关运算符 + - / * %
  • 字符连接符 +
  • 逻辑运算符 && ||
  • 二元运算符 & | ^
  • 一元运算符 + - ! ~
  • 移位运算符 >> >>> <<
  • 比较运算符 == > < >= <= (注: < 运算符需要写成 &lt;
  • instance of
  • ()
  • 字符、字符串、数字、null
  • 强转
  • 方法调用
  • 属性访问
  • 数组访问 []
  • 三元元素符 ?:

举例:

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

2 不可用操作

下面操作只能在代码里使用,不能用于表达式语法:

this
super
new

3 空结合运算符( ??

空结合运算符左边表达式如果不为空,使用左边表达式结果,否则使用右边表达式结果

android:text="@{user.displayName ?? user.lastName}"
复制代码

它等价于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"
复制代码

4 属性引用

使用绑定表达式,可以引用一个类的成员变量、get 方法、 ObservableField

android:text="@{user.lastName}"
复制代码

5 避免空指针异常

生成的绑定类会自动判空从而避免空指针异常。比如,表达式 @{user.name} ,如果 user 是空, user.name 会被赋予默认值 null 。如果引用的是 user.ageage 的类型是 int ,那么会使用 0 作为默认值。

6 集合

常用的集合,如数组、list、sparse list、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]}"
复制代码

注:为了能正确解析 XML,需要将 < 替换为 &lt; 。如 List<String> 应该写成 List&lt;String>

访问 map 中的元素,除了可以使用 @{map[key]} ,也可以使用 @{map.key}

7 字符串的写法

可以使用单引号包裹属性,在表达式中使用双引号,如下所示:

android:text='@{map["firstName"]}'
复制代码

也可以使用双引号包裹属性,在表达式中使用单引号,如下所示:

android:text="@{map[`firstName`]}"
复制代码

8 访问资源

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

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
复制代码

9 事件处理

Data Binding 可以让我们在表达式中处理 view 分发的事件,比如 onClick() 。属性的名字取决于监听器方法的名字,比如, View.OnClickListener 有一个 onClick() 方法,所以对应的属性名就是 android:onClick

处理 view 事件有两种方法:

null
listener binding
  • 方法引用

事件可以和方法直接绑定,这种方式与 android:onClick 可以和 activity 中一个方法绑定很类似。这种方式的优势是,绑定表达式是在编译期间处理的,如果方法不存在或者签名不匹配,会直接报错。

和监听器绑定不同的是,方法引用的方式会在数据绑定的时候创建监听器,监听器绑定则是在收到事件的时候创建监听器。

示例如下:

class MyHandlers {
    fun 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.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>
复制代码

注: 表达式中的方法签名必须与监听器中的方法签名一致。

  • 监听器绑定( listener binding

监听器绑定的方式,只有在收到事件的时候才会执行。它和方法引用很像,但是它可以使用任意的表达式。这个特性在 Gradle 2.0 及以后可以使用。

方法引用的方式,要求方法的签名必须和监听器的方法签名一致。但是监听器绑定的方式,只要求返回值一致即可。例如,假如下面的 Presenter 类有一个 onSaveClick() 方法:

class Presenter {
fun onSaveClick(task: Task){}
}
复制代码

onSaveClick() 可以与 android:onClick 绑定,如下所示:

<?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>
复制代码

在上面这个例子中,我们没有定义 view 参数。监听器绑定提供了两种选择:要么忽略所有参数,要么显式写出所有参数。如果你喜欢写出参数,如下所示:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"
复制代码

如果你需要使用这些参数,如下所示:

class Presenter {
    fun onSaveClick(view: View, task: Task){}
}
复制代码
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
复制代码

同时,也可以有多个参数,如下所示:

class Presenter {
    fun onCompletedChanged(task: Task, completed: Boolean){}
}
复制代码
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
      android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
复制代码

此外,如果你监听的事件返回值不是 void ,那么你的表达式也需要返回相同的返回值。如下所示:

class Presenter {
    fun onLongClick(view: View, task: Task): Boolean { }
}
复制代码
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
复制代码

如果表达式不能正常执行,那么会返回默认值,引用类型返回 null,int 类型返回 0,布尔类型返回 false。

Imports、variables、includes

Data Binding 提供了诸如 importsvariablesincludes 等特性。 imports 用于导入所需要的类,方便引用; variable 用于定义一个变量,方便在绑定表达式中使用。 includes 使我们可以复用布局。

  • Imposts

下面这个例子展示了导入 View 这个类到布局文件中:

<data>
    <import type="android.view.View"/>
</data>
复制代码

导入的目的就是为了方便在绑定表达式中使用。下面的例子展示了在表达式中引用 View 类的两个常量 VISIBLEGOEN

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
复制代码
    • 类型别名

如果导入的两个类名字相同,可以为其中一个或两个起别名以区分:

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

注: java.lang.* 下面的类会自动导入。

  • Variables

变量的声明用于在绑定表达式中使用,如下所示,声明了 userimagenote 三个变量:

<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>
复制代码

自动生成的绑定类,包含了这些变量的 get 和 set 方法。这些变量都会有默认值,引用类型默认值是 null ,int 类型默认值是 0,布尔类型默认值是 false。

  • Includes

变量可以传递给 include 布局,如下所示, user 变量传递给了 name.xmlcontact.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>
复制代码

Data binding 不支持 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>
复制代码

以上所述就是小编给大家介绍的《Data Binding 系列(三)布局和绑定表达式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Visual Thinking

Visual Thinking

Colin Ware / Morgan Kaufmann / 2008-4-18 / USD 49.95

Increasingly, designers need to present information in ways that aid their audiences thinking process. Fortunately, results from the relatively new science of human visual perception provide valuable ......一起来看看 《Visual Thinking》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具