美文网首页
Android 官方架构组件-数据绑定DataBinding

Android 官方架构组件-数据绑定DataBinding

作者: R7_Perfect | 来源:发表于2019-10-25 15:41 被阅读0次

环境运行要求 Android 4.0 (API level 14)及以上
Android Gradle插件要求1.5.0及以上

将dataBinding元素添加App的build.gradle文件中

android {
    ...
    dataBinding {
        enabled = true
    }
}

在AndroidSutdio的布局编辑窗口,例如,以下示例中声明的TextView小部件上显示my_default值:

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

官方例子

布局和绑定表达式

数据绑定布局文件略有不同,它们以layout的根标记开始,后跟一个数据元素和一个视图根元素。 此视图元素是您的根将位于非绑定布局文件中的元素。 以下代码显示了一个示例布局文件:

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

让我们来看看User类的定义

data class User(val firstName: String, val lastName: String)

这种数据为只读,通常只取一次,后面不会再取
用于android:text属性的表达式@ {user.firstName}用于访问前一类中的firstName字段和后一类中的getFirstName()方法

绑定数据

程序会自动为每个布局文件生成一个绑定类。 默认情况下,类的名称基于布局文件的名称,将其转换为Pascal大小写并向其添加Binding后缀。 上面的布局文件名是activity_main.xml,因此相应的生成类是ActivityMainBinding。

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

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

    binding.user = User("Test", "User")
}

或者,可以使用LayoutInflater获取视图,如以下示例所示:

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())

如果在Fragment,ListView或RecyclerView适配器中使用数据绑定项,如以下代码示例所示

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

表达式

+ - / * %
String +
&& ||
& | ^
+ - ! ~
>> >>> <<
== > < >= <= (注意 < 需要转为 <)
instanceof
Grouping ()
Literals - character, String, numeric, null
Cast
Method calls
Field access
[]
?:

例子:

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

属性

避免空指针

生成的数据绑定代码自动避免空指针异常。 例如,在表达式@ {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&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;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]}"

上面例子中的 @{map[key]}可以写成@{map.key}

String

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

android:text='@{map["firstName"]}'
或者
android:text="@{map[`firstName`]}"
Resource资源

示例

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

String格式和复数可以这样写:

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

//复数形式
  Have an orange
  Have %d oranges

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

事件处理

可以通过定义接口,定义事件处理,但是部分关键接口名和系统不能冲突:
android:onClick
android:onSearchClick
android:onZoomIn
android:onZoomOut

有两种方式:

方式一 直接绑定

事件可以直接绑定到处理程序方法,类似于可以将android:onClick分配给活动中的方法的方式。 与View onClick属性相比,一个主要优点是表达式是在编译时处理的,因此,如果该方法不存在或其签名不正确,则会收到编译时错误。
和Listener绑定之间的主要区别在于,实际的Listener实现是在绑定数据时创建的,而不是在事件触发时创建的。

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绑定
class Presenter {
    fun onSaveClick(task: Task){}
}

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

使用回调时,databinding将自动创建必要的Listener并将其注册为事件。 当View触发事件时,databinding将传递给方法。
上述方法也可以这样写

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

如果想用view回调:

class Presenter {
    fun onSaveClick(view: View, task: Task){}
}

android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

lambda表达式

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

android:onLongClick返回值为bool型,所以表达式也为bool值
如果由于空对象而无法对表达式求值,则数据绑定将返回该类型的默认值。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

Imports, variables, and includes

databinding库提供了一些功能,例如导入,变量和包含。 Imports可以轻松引用布局文件中的类。 variables能够描述可在绑定表达式中使用的属性。includes在整个应用程序中重复使用复杂的布局。

Imports
<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&lt;User>"/>
</data>

强转:

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

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

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

当针对各种配置(例如,横向或纵向)有不同的布局文件时,将合并变量。 这些布局文件之间不得有冲突的变量定义。

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

会根据需要生成一个特殊的名为context的变量,用于绑定表达式。 context的值是根视图的getContext()方法中的Context对象。 上下文变量被具有该名称的显式变量声明覆盖。

Includes

以下示例显示了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>

数据绑定不支持包含作为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>

相关文章

网友评论

      本文标题:Android 官方架构组件-数据绑定DataBinding

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