Data Binding
是谷歌提供的 android 数据绑定库,为了而方便开发者实现 MVVM 的架构模式,使用它可以避免我们写大量的findViewByID
,降低代码的耦合性。
官方介绍(需翻墙)
一、使用环境要求
通过查看官方文档,改数据绑定库的使用环境要求如下:
- 下载 SDK Manager 中的支持库: Support repository;
- android studio 版本在1.3之后;
- gradle 版本在1.5.0-alpha1之后;
- android sdk在android 2.1(API level7 +)以后。
二、具体的使用
2.1、配置 data binding.
在 model 的gradle中的 android
节点下添加如下代码:
1 2 3
| dataBinding{ enabled = true }
|
2.2、 建立数据对象
添加一个POJO类,这里定义的是 User类,添加3个变量(uname,usex,uage)及相应的get,set方法,方便接下来与布局文件惊醒绑定。
2.3、 修改布局文件
使用databinding后,布局文件根节点不在是简单的LinearLayout,RelativeLayout等ViewGroup,而是 Layout
,同时还增加了 data
元素,来为ui控件提供数据。基本局如下:
1 2 3 4 5 6 7 8
| <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> </data> <!--原先的根节点--> <LinearLayout> .... </LinearLayout> </layout>
|
下面简单举例说明,显示用户信息,包括用户名,性别和年龄,布局文件名为activity_main
,使用 databinding 的布局代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="cn.imtianx.databindingdemo.bean.User"> </variable> </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="@{user.uname}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.usex}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.uage}"/> </LinearLayout> </layout>
|
该布局主要是三个TextView,来显示信息,在data
标签中添加了变量variable
,其中name
是变量名,type
使我们定义的java类(注:需要写完整的包名),通过@{}
来为 TextView设置显示的文本。
此外,这里的data
也可以用import
进行导入,如下:
1 2 3 4 5 6
| <data> <import type="cn.imtianx.databindingdemo.bean.User"/> <variable name="user" type="User"/> </data>
|
如果要使用String
等java.lang.*
下的类,则可以直接使用。
2.4、绑定变量数据
编译项目即可根据布局文件名生成相关的Binding类,生成规则是按布局文件名,去掉’_‘,按驼峰法则,并在末尾添加Binding
。如,这里的布局文件名为activity_main
,则生成的数据绑定类为ActivityMainBinding
,它存放在包名.databinding
下,然后再 activity 的onCreate方法中设置变量,代码如如下:
1 2 3 4 5 6 7 8 9
| @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding mainBinding = DataBindingUtil .setContentView(this, R.layout.activity_main); User user = new User("imtianx", "男", "20"); mainBinding.setUser(user); }
|
注意: 这里的 ActivityMainBinding 类会根据布局文件中的 variable 生成相应的 set
方法。
至此,一个简单的数据绑定就实现了,运行程序,会依次显示user中设置的三个字段的值。
三、设置见监听事件
这里可以通过两种方式进行绑定事件:方法引用和监听器引用。如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Presenter { public void onTextChanged(CharSequence s, int start, int before, int count) { mUserBean.setName(s.toString()); mBinding.setUser(mUserBean); } public void onClick(View view) { Toast.makeText(MainActivity.this, "点击了名字", Toast.LENGTH_SHORT).show(); } public void onClickListenerBinding(UserBean bean) { Toast.makeText(MainActivity.this, bean.getSex(), Toast.LENGTH_SHORT).show(); } }
|
使用时首先在xml的data标签下添加变量
1 2 3 4
| <variable name="presenter" type="cn.imtianx.databindingdemo.MainActivity.Presenter"> </variable>
|
3.1 方法引用
必须使用android 已有的监听的方法名及其参数,如上面的onTextChanged,onClick方法,具体的调用如下:
1
| android:onClick="@{presenter.onClick}"
|
3.2 监听器引用
可以方便的丛xml中向java代码中传递数据,可使用lambda表达式,如onClickListenerBinding,具体调用如下:
1
| android:onClick="@{()->presenter.onClickListenerBinding(user)}"
|
采用了lambda表达式的格式。
四、在Fragment中的用法
布局文件与上一个一样,在 Fragment 的 onCreateView 中设置相关的属性,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| private ActivityMainBinding mMainBinding; private User user; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.activity_main, container, false); // 方式1,直接用默认生成的Binding类绑定 mMainBinding = ActivityMainBinding.bind(view); // 方式2,向上转成ViewDataBinding类型 // ViewDataBinding viewDataBinding = ActivityMainBinding.bind(view); // mMainBinding = (ActivityMainBinding) viewDataBinding; //方式3,使用生成的Binding的inflater, // mMainBinding = ActivityMainBinding.inflate(inflater); //方式4,使用生成的Binding的inflater,类似Inflater api // mMainBinding = ActivityMainBinding.inflate(inflater, container, false); //方式5,某种情况无法生存默认Binding的情况下,并且把对应的layout传入 // ViewDataBinding viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.activity_main, container, false); // mMainBinding = (ActivityMainBinding) viewDataBinding; //方式6,某种情况无法生存默认Binding的情况下 // ViewDataBinding viewDataBinding = DataBindingUtil.bind(view); user = new User("imtianx", "男", "20"); mMainBinding.setUser(user); return view; }
|
总之,它都需要初始化Binding类,初始化model类和数据绑定。
五 、高级用法
5.1. 使用类方法
首先在布局文件的data
使用import
导入方法所在的类的全路径,然后再选要的地方调用,具体使用和java一样。
5.2. 类型别名
在开发中可能会遇到两个用名的类,如果在data
下同时导入他们,改如何解决?这里不用担心,可以在import
节点下添加alias
属性,来区别。如下示例:
1 2 3 4 5 6 7 8 9 10
| <data> <import type="cn.imtianx.databindingdemo.bean.User" alias="User1"/> <import type="cn.imtianx.databindingdemo.model.User" /> <variable name="user" type="User1"/> <variable name="user2" type="User"/> </data>
|
5.3. Null Coalescing 运算符
这个和java中的三木表达式一样
1 2 3 4
| <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.uage ??user.uage}"/>
|
它等价于:
1 2 3 4
| <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.uage!=null?user.uage:0}"/>
|
5.4. 属性值
使用@{}
在xml中使用java 中定义的一些属性值,如下给visibility 设置值,注意需要到如View类,
1 2 3 4 5
| <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.display? View.VISIBLE:View.GONE}" android:text="@{user.uage"/>
|
5.5. 使用资源数据
设置padding
的值,需要的dime文件中添加largePadding和smallPadding的item。对于引用 String、drawable
等资源类似。
1 2 3 4 5
| <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@{user.display?(int)@dimen/largePadding : (int)@dimen/smallPadding}" android:text="@{user.uage}"/>
|
5.6. 使用include
使用命名空间来传递variable,将当前 variable 的值传递给 include 进来 的布局中。
为 layout 添加命名空间:
1
| xmlns:bind="http://schemas.android.com/apk/res-auto"
|
在include
中使用:
1 2 3
| <include layout="@layout/layout_user" bind:user="@{user}">
|
注意在 layout_user.xml
中也要在 variable 中 定义添加 user 变量。
5.7. 使用表达式
java 中的表达式,在这里也是支持的,
- 数学 + - / * %
- 字符串连接 +
- 逻辑 && ||
- 二进制 & | ^
- 一元运算 + - ! ~
- 移位 >> >>> <<
- 比较 == > < >= <=
- instanceof
….
Data Binding代码生成时自动检查是否为null来避免出现null pointer exceptions
错误,String
类型默认值是null
,int
类型默认值是 0
,boolean
类型默认值是 false
。
5.8. 集合
常用的集合:arrays、lists、sparse,lists以及maps,为了简便都可以使用[]
来访问。注意,在使用泛型时<
需要转义,用<
代替,否则会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <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]}"
|
5.9.Data对象(数据绑定)
Data Binding 的强大之处主要在于双向数据绑定,当POJO对象发生变化时,通知改变Data对象,已达到更新UI的效果。有三种不同的数据变化通知机制:Observable
对象、ObservableFields
以及observable collections
。
这里以 Observable 为例,更改User类。使其继承BaseObservable
,在getter
方法前添加Bindable
注解,在setter
方法中调用notifyPropertyChanged
进行更新数据。如果只更新某一字段,只需将该字段设置为ObservableFields
类型的,如boolean,可用ObservableBoolean代替,但对其的使用需要通过get和set方法。具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public class User extends BaseObservable{ private String uname; private String usex; private String uage; private boolean isDisplay; public User() { } public User(String uname, String usex, String uage, boolean isDisplay) { this.uname = uname; this.usex = usex; this.uage = uage; this.isDisplay = isDisplay; } @Bindable public String getUname() { return uname; } public void setUname(String uname) { this.uname = uname; notifyPropertyChanged(BR.uname); } @Bindable public String getUsex() { return usex; } public void setUsex(String usex) { this.usex = usex; notifyPropertyChanged(BR.usex); } @Bindable public String getUage() { return uage; } public void setUage(String uage) { this.uage = uage; notifyPropertyChanged(BR.uage); } @Bindable public boolean isDisplay() { return isDisplay; } public void setDisplay(boolean display) { isDisplay = display; notifyPropertyChanged(BR.display); }
|
在编译期间,Bindable注解在BR(与R文件类似)类文件中生成一个Entry。BR类文件会在模块包内生成。如果用于Data类的基类不能改变,Observable接口通过方便的PropertyChangeRegistry来实现用于储存和有效地通知监听器。
Data Binding的基本用法已经介绍完了,但它 的使用知识点较多,暂且写到这里,对于它在ListView/RecyclerView中的用法、事件处理等稍后再做介绍。
文中部分资料来源于页底的参考资料。
参考资料:
- https://github.com/LyndonChin/MasteringAndroidDataBinding
- http://www.jianshu.com/p/b1df61a4df77
- https://realm.io/cn/news/data-binding-android-boyar-mount/?utm_source=tuicool&utm_medium=referral
上一篇:Android Bluetooth 蓝牙技术初体验
下一篇:android 异常捕获-UncaughtExceptionHandler