几年前,数据绑定在便已在前端界风生水起,Angular.js、React.js、vue.js等热门前端框架都具有这种能力。Android端的开源库butterknife/Anotation/dragger2等等也很是好用且广受支持;前端
数据绑定简单来讲,就是经过某种机制,把代码中的数据和xml(UI)绑定起来,双方都能对数据进行操做,而且在数据发生变化的时候,自动刷新数据。vue
在2015年的谷歌IO大会上,Android UI Toolkit团队发布了DataBinding 框架, 只须要在gradle配置文件里添加短短的三行,就能用上数据绑定,不须要依赖第三方库android
在app下的build.grade加上以下:编程
android { .... dataBinding { enabled = true } }
能有效提升开发效率,减小大量须要手动编写的胶水代码(如findViewById,setOnClickListener);前端框架
高性能(绝大部分的工做在编译期完成,避免运行时使用反射);数据结构
使用灵活(可使用表达式在布局里进行必定的逻辑运算);app
具备IDE支持(语法高亮、自动补全,语法错误标记)。框架
使用数据绑定的布局文件以<layout>标签做为根节点,代表这是个数据绑定的布局,修改后数据绑定框架会生成对应的*Binding类ide
,如content_main.xml会生成ContentMainBinding类,即默认规则是:单词首字母大写,移除下划线,并在最后添加上Binding。组件化
,以前的布局文件是以LinearLayout、RelativeLayout…几大布局为根标签,使用Databinding的布局文件以下:(@单项绑定 ; @= 双向绑定)
<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>
布局文件结构
上面布局文件的结构要写对为根节点,里面包含一个节点和之前布局写法的布局组件。
data节点下有节点,variable有name、type属性,name为本身定义的type类型的对象,用来在布局文件中引用;type就是你要用到的类啦,好比上面写的JavaBean类或者点击事件的android.view.View.OnClickListener接口类。data节点下还有一个节点,写法以下:
<data> <import type="com.test.qby.newtestapplication.model.TestModel"/> <variable name="test" type="TestModel" /> </data>
此时variable属性type的值为import属性type的类名TestModel,这就可能会出现若是项目中在不一样包名下有相同类名的类须要同时引用怎么办?google也给了解决办法:import还有一个alias属性,给导入的类起别名,好比:
<data> <import type="com.test.qby.newtestapplication.model.TestModel" alias="ModelTest"/> <import type="com.test.qby.newtestapplication.viewmodel.TestModel"/> </data>
不想设置variable的话,引用的时候直接用@{ModelTest.getName()}和@{TestModel.getName()},可是须要getName()为static方法,这样就能够解决类名冲突了。
布局文件中对data数据的引用:
android:text="@{@string/test+test.name}"
在<layout>标签内部添加<data>标签,便可声明数据。给<data>标签添加class属性能够改变生成的*Binding类的名字,如使用<data class="ContentMain">将其改成ContentMain。
数据标签内部经过<variable>标签声明变量,经过<import>标签导入辅助类,为了不同名冲突,可使用alias属性指定一个别名。
代码调用
页面中调用方式也有几种
//1 ActivityMainTestBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main_test); //2 View inflate = LayoutInflater.from(this).inflate(R.layout.activity_main_test, null); ActivityMainTestBinding bind = DataBindingUtil.bind(inflate); //3 ActivityMainTestBinding inflate = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.activity_main_test, null, false);
根据状况本身选择方式就行,如:
在activity下:private void initView() {binding = DataBindingUtil.setContentView(this, R.layout.activity_map); } 在fragment下: databinding写法: @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate(inflater, R.layout.activity_connection_pager, container, false); onClick(); initData(); return binding.getRoot(); } 原写法: @Override protected View initView() { view = LayoutInflater.from(getContext()).inflate(R.layout.pager_map, null, false); bindViews(); init(); return view; }
虽然数据绑定支持的POJO(Pure Old Java Object,普通Java类,指仅具备一部分getter/setter方法的类),但对POJO对象的数据更新并不会同步更新UI。为了实现自动更新,能够选择:
继承自BaseObservable,给getter加上@Bindable注解,并在setter中实现域的变更通知。
若是数据类没法继承BaseObservable,变更通知能够用PropertyChangeRegistry来实现。
最后一种是使用Observable域,对数据存取经过ObservableField<T>的get、set方法调用实现。ObservableField<T>是泛型类,对于基础类型,有对应的ObservableInt、ObservableLong、ObservableShort等可供使用;另外对于容器,每次只会更新其中的一个项,而不是整个更新,所以还有对应的ObservableArrayList、ObservableArrayMap可供使用。
第一种:JavaBean继承BaseObservable
public class TestModel extends BaseObservable{ private String name; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } }
JavaBean继承BaseObservable;getter方法添加@Bindable注解;settter方法内部添加通知notifyPropertyChanged;
第二种:JavaBean中字段改成ObservableField
Databinding提供的类包括:ObservableField,ObservableBoolean,ObservableByte,ObservableChar,ObservableShort,ObservableInt,ObservableLong,ObservableFloat,ObservableDouble,ObservableParcelable。
public class TestModel { public ObservableField<String> name = new ObservableField<>(); public ObservableBoolean name = new ObservableBoolean(); }
别忘了字段修饰符要public,调用直接使用set()
TestModel testModel = new TestModel(); testModel.name.set("这是测试文字"); testModel.isTrue.set(true);
布局中引用方式与其余的没区别,在代码中获取值就直接调用get()
String name = testModel.name.get(); boolean isTrue = testModel.isTrue.get();
这种方法虽然看着没有getter/setter方法,可是它是在内部实现过了。
第三种:不使用JavaBean,建立Observable Clollections
这种方式是为了避免建立Javabean,而使用动态数据结构来更新UI。Databinding提供了ObservableArrayMap,ObservableArrayList两个类来实现。
使用代码动态建立
ObservableArrayList<Object> observableList = new ObservableArrayList<>(); observableList.add("databindingList"); observableList.add(12); binding.setListIndex(1); binding.setObservableList(observableList); ObservableArrayMap<String,Object> observableMap = new ObservableArrayMap<>(); observableMap.put("hash1", "databindingMap"); observableMap.put("hash2", 16); observableMap.put("hash3", "显示"); binding.setKey("hash3"); binding.setObservableMap(observableMap);
使用方式跟使用ArrayList、HashMap没什么区别
<variable name="list" type="android.databinding.ObservableArrayList<Object>" /> <variable name="map" type="android.databinding.ObservableArrayMap<String,Object>" /> <variable name="listIndex" type="int" /> <variable name="key" type="String" /> </data><LinearLayout android:orientation="vertical" style="@style/MathchMathch"> <TextView style="@style/MatchWrap" android:text="@{String.valueOf(list[listIndex])}"/> <TextView style="@style/MatchWrap" android:text="@{String.valueOf(map[key])}"/> </LinearLayout>
这个就很简单了,若是是使用语法问题,log会有相应的记录,能够略过log中前面众多databinding类文件不存在的提示,只看最后俩三行就能够直接明了的找到缘由。
若是,你在最后俩三行也没找到明确的提示你错误缘由,(什么是明确?就是你不知道你的代码哪一行哪一个地方出现错误),那么,你应该不久前手抖了一下,删除或者增长了布局文件某一个地方,形成xml有语法错误,可是坑爹的是使用layout包裹后的布局文件根本不会提示你你的xml布局有问题。
这样就是找不到新增长的view,即便你build也依然找不到这个id,首先确保本身id写正确的,布局也是正确的,而后仅需重启AndroidStudio便可貌似有时binding类不会随着布局文件实时更新.
使用数据绑定,实现了数据和表现的分离,结合响应式编程框架RxJava、RxAndroid,编码体验和效率能还能进一步提升。
因为数据绑定实现了数据和表现的分离,由Data Binding框架对接UI,能够经过自定义Adapter,干预某些属性的属性读取和设置,好比拦截图片资源的加载(换肤)、动态替换字符(翻译)等功能。
方便UI复用,Android上进行UI组件化的时候,能够在布局的层次上进行复用,业务无关的UI逻辑也能一块儿打包,同时保持对外接口(数据模型)简单,学习接入成本更小。
DataBinding在xml提供了丰富的操做符,可是因为Android studio天生的xml语法检查的贫弱,xml布局中的表达式逻辑错误,不能准肯定位,致使debug难度增长,事实上一些BindingAdapter的错误在build的时候也会被提示xml错误。
本文参考: