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 系列(三)布局和绑定表达式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

程序员的修炼

程序员的修炼

Jeff Atwood / 陆其明、杨溢 / 人民邮电出版社 / 2014-4 / 45.00元

《程序员的修炼——从优秀到卓越》是《高效能程序员的修炼》的姊妹篇,包含了Coding Horror博客中的精华文章。全书分为8章,涵盖了时间管理、编程方法、Web设计、测试、用户需求、互联网、游戏编程以及技术阅读等方面的话题。作者选取的话题,无一不是程序员职业生涯中的痛点。很多文章在博客和网络上的点击率和回帖率居高不下。 Jeff Atwood于2004年创办Coding Horror博客(......一起来看看 《程序员的修炼》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具