DataBinding可以做些什么
- 在xml中操作数据和事件
- 自动绑定id,省去findViewById
- 实现View和数据双向绑定
引入
在app或者lib的build.gradle中添加
android {
dataBinding {
enabled = true
}
}
2020.5.12更新
在kotlin中使用时,为了支持注解处理,还需要另外添加
apply plugin: 'kotlin-kapt'
简单使用
在原本的布局文件底层添加<layout>标签,实现对xml文件的DataBinding化。
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
这时,在Activity中,就可以对DataBinding进行初始化了。ActivityMainBinding是根据xml自动生成的一个类。名称是由xml文件名驼峰化+Binding后缀而来。如activity_main--->ActivityMainBinding。
如果未找到类,rebuild就可以了。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.tvName.setText("hello world");
}
}
在给TextView设置id后,DataBinding会自动进行绑定,binding.tvName对应id为tv_name的TextView,同样是对命名进行了驼峰化。这样就省去了findViewById。相对于ButterKinfe,也省去了BindView的代码。
至此,是DataBinding最最简单的使用,即省去findViewById。
在xml中绑定数据
为了方便理解,这里定义了一个UserBean类(注意,set方法必须要有),会在下面使用。
public class UserBean {
private String name;
public UserBean(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在xml中添加<data>标签
<data>标签下添加<variable>标签
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<data>
<variable
name="userBean"
type="com.qianfanyun.lib.recyclerview.UserBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{userBean.name}"
/>
</LinearLayout>
</layout>
<data>固定在<layout>的下一层,在<data>内可以定义各种变量<variable>。
<variable>有两个属性
- name 变量名,可以随意取,在这个布局中不重复就可以了。
- type 这个变量的类型,对应于某个类。
<TextView>中text属性的值为@{userBean.name}
userBean是上面定义的变量,我们要引用这个变量中的值,只需要用@{}包裹,并且调用 [上面定义的变量名.属性名]。最终DataBinding会调用到这个对象的对应属性的get方法。
在java代码中,可以把这个对象设置进去。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setUserBean(new UserBean("ArcherYc"));
}
最终在这个页面中会显示ArcherYc这个文字。
通过变量id设置值:
binding.setVariable(BR.userBean,new UserBean("ArcherYc"));
稍微总结下DataBinding的基本结构
<layout>
<data>
</data>
<root view>
</root view>
</layout>
支持的表达式
在@{}
内可以使用一下表达式
- Mathematical + - / * %
- String concatenation +
- Logical && ||
- Binary & | ^
- Unary + - ! ~
- Shift >> >>> <<
- Comparison == > < >= <= (Note that < needs to be escaped as <)
- instanceof
- Grouping ()
- Literals - character, String, numeric, null
- Cast
- Method calls
- Field access
- Array access []
- Ternary operator ?:
列如:
表达式中存在双引号时,最外层用单引号。
android:text='@{"name:"+user.name}'
2020.5.15新增
Null 合并运算符
如果左边运算数不是 null,则 Null 合并运算符 (??) 选择左边运算数,如果左边运算数为 ,则选择右边运算数。
android:text="@{user.displayName ?? user.lastName}"
这在功能上等效于:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
空安全
DataBinding在variable对象为空时,会在用到variable的地方填充默认值,所以不需要担心报空指针异常。
使用资源
在实际项目中,可能会出现一个布局的参数需要根据类型,动态改变,这时使用下面这种方法可以方便的改变。这样就避免了使用LayoutParams来修改。
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
事件传递
两种方案设置处理事件
- 方法引用 类似于OnClick调用Activity中的方法。
- 监听绑定
方法引用
定义一个方法,用于处理点击事件。有且仅有一个参数View。方法必须为public。
在绑定事件时调用
public class Handler{
public void onClick(View view){
Toast.makeText(MainActivity.this, "onClick", Toast.LENGTH_SHORT).show();
}
}
在data中申明方法所在类的对象
<data>
<variable
name="handler"
type="com.qianfanyun.databinding.MainActivity.Handler"/>
</data>
设置onClick
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{handler::onClick}"
/>
在DataBinding中设置对象
binding.setHandler(new Handler());
方法引用是在把处理对象设置进去的时候,设置了监听。即setHandler。
监听绑定
申明一个处理事件的方法。不需要特定参数,可以自定义参数。方法必须为public。
在事件发生时调用
public class Handler{
public void onClick(String name){
Toast.makeText(MainActivity.this, name, Toast.LENGTH_SHORT).show();
}
}
在data中申明方法所在类的对象
<data>
<variable
name="handler"
type="com.qianfanyun.databinding.MainActivity.Handler"/>
</data>
设置onClick ()->为固定格式 后面的表达式与java中使用语法相同
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick='@{()->handler.onClick("ArcherYc")}'
/>
在DataBinding中设置对象
binding.setHandler(new Handler());
通过这种方式设置监听,是在事件发生时调用的。
处理的方法可以添加自定义参数。
支持传递调用处的对象,比如:
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
public class Presenter {
public void onSaveClick(View view, Task task){}
}
使用void来表示不处理
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
导入类
在<data>内可以导入类在表达式中使用
这里以导入View类为例
<data>
<import type="android.view.View"/>
<variable
name="user"
type="com.qianfanyun.lib.recyclerview.UserBean"/>
</data>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ArcherYc"
android:visibility="@{user.student?View.VISIBLE:View.GONE}"
/>
导入的类可以调用其静态公共方法
<import type="com.qianfanyun.databinding.StringUtils"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ArcherYc"
android:visibility="@{!StringUtils.isEmpty(user.name)?View.VISIBLE:View.GONE}"
/>
当名称产生冲突时,可以增加alias属性来解决冲突
<import type="com.qianfanyun.databinding.View"
alias="myView"/>
<import type="android.view.View"/>
java.lang.*是自动导入的
include
DataBinding支持include。include中要使用变量,需要进行传递。
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<include
android:id="@+id/include"
layout="@layout/name"
bind:user="@{user}" />
</LinearLayout>
在include的布局中使用变量
<layout>
<data>
<variable
name="user"
type="com.qianfanyun.lib.recyclerview.UserBean"/>
</data>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
/>
</LinearLayout>
</layout>
获取include中的变量对象。必须要先给<include>标签设置id。
binding.include.tvName.setText("ArcherYc");
网友评论