环境运行要求 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<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]}"
上面例子中的 @{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<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>
网友评论