数据绑定库
一、搭建环境
build.grage 添加 dataBinding
android{
...
dataBinding{
enabled = true
}
}
二、示例项目:
xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user1"
type="union.com.viewgroup.jetpack.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user1.firstName}" />
</LinearLayout>
</layout>
activity 代码:
package union.com.viewgroup.jetpack;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import union.com.viewgroup.R;
import union.com.viewgroup.databinding.ActivityDataBindingBinding;
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDataBindingBinding binding = DataBindingUtil.setContentView(MyActivity.this, R.layout.activity_data_binding);
binding.setUser1(new User("张","三"));
}
}
在运行时,您可以使用 LayoutInflater 来获取视图 代码如下:
ActivityDataBindingBinding activityMainBinding = ActivityDataBindingBinding.inflate(getLayoutInflater());
如果你想使用数据绑定绑定Fragment ,ListView 或者 RecyclerView adapter 你可以使用inflate 方法 来绑定类名,代码如下:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
三、表达式
1、+ - / * %
2、 字符串拼接 +
3、 逻辑运算符 && ||
4、二进制云运算符 & | ^
5、 单目运算符 + - ! ~
6、移位运算符 >> >>> <<
7、比较运算符 == > < >= <= 注意 < 需要转换为 <
8、 instanceof
9、分组
10、文字字符 ,字符串 、数字 、null
11、Cast
12 、字段
13、数组
14、?:
example:
android:text="@{String.valueOf(index+ 1)}"
android:visibility = "@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName="@{"image_"+id}"
四、不支持的操作
this、 super、 new 、显示泛型调用
空合并运算符
android:text = "@{user.firstName ?? user.lastName }"
在功能上等效于:
android:text= "@{user.firstName!= null ? user.firsetName : user.lastName }"
五、避免空指针
生成的数据绑定代码自动检查null
值,并避免空指针异常。例如,在表达式中@{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]}"
**注意:**为了使XML在语法上正确,必须对<
字符进行转义 。例如:List<String>
写 List<String>
。
您也可以使用object.key
符号在地图中引用值。例如,@{map[key]}
在上面的示例中,可以将替换为 @{map.key}
。
七、字符串文字
您可以使用单引号将属性值引起来,这允许您在表达式中使用双引号,如以下示例所示:
android:text='@{map["firstName"]}'
也可以使用双引号将属性值引起来。这样做时,字符串文字应该用反引号引起来```:
android:text="@{map['firsetName ']}"
八、资源
您可以使用一下语法访问表达式中的资源
android:padding = "@{large ? @dimen/largePadding : @dimen/smallPadding}"
格式字符串和复数形式可以通过提供参数来评估:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
当复数带有多个参数时,应传递所有参数:
Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
某些资源需要显式类型评估,如下表所示:
类型 | 正常参考 | 表达式参考 |
---|---|---|
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |
九、事件处理
数据绑定允许你写一个表达式来捕获时间 例如 onClick 方法
属性和 事件对应如下:
Class | Listener setter | Attribute |
---|---|---|
SearchView | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
ZoomControls | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
ZoomControls | setOnZoomOutClickListener(View.onClickListener) | android:onZoomOut |
您可以使用以下机制来处理事件:
方法引用:
在表达式中,您可以引用符合侦听器方法签名的方法。当表达式对方法引用求值时,数据绑定将方法引用和所有者对象包装在侦听器中,然后在目标视图上设置该侦听器。如果表达式的计算结果为null
,则数据绑定不会创建侦听器,null
而是会设置侦听器。
帧听器绑定:
这是事件发生时评估的lambda表达式。数据绑定始终创建一个侦听器,并在视图上进行设置。调度事件后,侦听器将评估lambda表达式。
方法引用和侦听器绑定之间的主要区别在于,实际的侦听器实现是在绑定数据时创建的,而不是在事件触发时创建的。如果您希望在事件发生时评估表达式,则应使用监听器绑定。
public void MyHandlers{
public void onClickFriend(View view){...}
}
xml 定义如下:
<?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>
**注意:**表达式中方法的签名必须与侦听器对象中方法的签名完全匹配。
帧听器绑定:
帧听器绑定是事件发生时运行的绑定表达式。它们类似于方法引用,但是它们使您可以运行任意数据绑定表达式。适用于Gradle 2.0版及更高版本的Android Gradle插件提供了此功能
在方法引用中,方法的参数必须与事件侦听器的参数匹配。在侦听器绑定中,只有您的返回值必须与侦听器的期望返回值匹配(除非期望返回void)。
public class Presenter{
public void onSaveClick(Task task){}
}
xml:
<?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>
在表达式中使用回调时,数据绑定将自动创建必要的侦听器并将其注册为事件。当视图触发事件时,数据绑定将评估给定的表达式。像在常规表达式中一样,在评估这些侦听表达式时,您仍然可以获得数据绑定的null 和线程安全
在上面的示例中,我们尚未定义view 传递参数onClick(View) 。侦听器绑定为侦听器参数提供了两种选择:您可以忽略方法所有的参数,也可以全部命名。如果你想命名参数,则可以在表达式中使用它们。例如,上面的表达式可以编写如下:
android:onClick= "@{(view)-> presenter.onSaveClick(task)}"
或者 在表达式中使用参数
public class Presenter {
public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
超过1个参数您可以使用lambda 表达式
十、导入、变量和 包含
数据绑定库提供了一些功能,例如导入,变量和包含。导入使您可以轻松引用布局文件中的类。变量使您能够描述可在绑定表达式中使用的属性。包括让您在整个应用程序中重复使用复杂的布局。
十、导入
导入使您可以轻松的引用布局文件中的类,就像在托管代码中一样。import 元素内部可以使用零个或多个元素data。
<import type="android.view.View" />
组件:
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user1.isDefault ? View.VISIBLE : View.GONE}"/>
当类冲突时,可以将其中一个类进行重命名。
<import type="android.view.View" alias="view" />
导入其他类
导入的类型可用作变量和表达式中的类型引用。以下示例显示User并List用作变量类型
<import type="union.com.viewgroup.jetpack.User" />
<variable
name="user1"
type="User" />
<import type="java.util.List"/>
<variable
name="list"
type="List<User" />
您也可以使用导入的类型来强制转换表达式的一部分。以下示例将connection
属性强制转换为类型User
:
<TextView android:text="@{((User)(user.connection)).lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
当在表达式中引用静态字段和方法时,也可以使用导入的类型。以下代码导入MyStringUtils
该类并引用其capitalize
方法:
<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"/>
就像在托管代码一样,java.lang.* 将自动导入。
变量
您可以variable
在元素内部使用多个元素data
。每个 variable
元素都描述了可以在布局上设置的属性,以在布局文件内的绑定表达式中使用。下面的示例声明的user
,image
和note
变量:
<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>
包括
通过使用应用程序名称空间和属性中的变量名称,可以将变量从包含的布局传递到包含布局的绑定中。以下示例显示user
了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>