DataBinding详解
来源:互联网 发布:淘宝well运动正吗 编辑:程序博客网 时间:2024/06/03 12:21
Databinding
介绍
Databinding数据绑定,简单的说,就是通过某种机制,把代码中的数据和xml(UI)绑定起来,双方都能对数据进行操作,并且在数据发生变化的时候,可以自动刷新数据。
数据绑定方式
单向绑定
单向绑定就是说数据的流向是单方面的,只能从代码流向UI;
双向绑定
双向绑定的数据流向是双向的,当业务代码中的数据改变时,UI上的数据能够得到刷新;当用户通过UI交互编辑了数据时,数据的变化也能自动的更新到业务代码中的数据上。
优势
1.性能很好,因为没有反射,而且性能比直接findViewById要高。
2.谷歌原生支持
3.代码简洁
4.减少代码线程切换更新UI
5.自动检查空指针
局限性
1.与某些技术冲突。比如dragger2,插件化技术,热修复技术。
2.databinding依赖与XML布局,databinding插件会根据xml文件内容生成代码,因此无法支持在代码中动态生成View
3.Databinding库要求安卓的gradle插件最低是1.5.0
4.Databinding要求安卓平台2.1(Api 7+)
5.Android Studio版本要在1.3之上
使用介绍
配置
在app级别的build.gradle文件中加入DataBinding元素,点击同步即可
android { .... dataBinding { enabled = true }}
DataBinding的布局文件格式
activity_main.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>
DataBinding的布局文件和原来的布局稍有些不同,它的根布局是一个layout标签,里边包含一个data标签和原来的根布局。
data标签中可以import类名
<data> <import type="com.example.peiyu_wang.databinding.entity.StudentObserve" /> <import type="android.view.View.OnClickListener" /> <variable name="stu" type="StudentObserve" /> <variable name="clicklistener" type="OnClickListener" /> </data><variable name="user" type="com.example.User"/>
在data标签中的user变量在你的布局文件中使用。
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}"/>
布局文件中的表达式要写在属性中,并写成”@{}”形式。这个例子中,TextView中的文字被设置成为了user的firstName属性。
点击build->makeProject
此时DataBinding就会自动生成ViewDataBinding类的子类,类名根据layout文件的名称而生成。如现在的layout文件名是activity_main.xml,因此生成的类名是ActivityMainBinding。能不能自定义生成类名?当然可以。
<data class="com.example.ContactItem"> ...</data>
android:text=”@{user.firstName}”,这里写的是user.firstName,其实是调用了user.getFirstName方法。更多介绍请看实现原理。。。
布局写好后,回到MainActivity
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); User user = new User("Test", "User"); binding.setUser(user);}
这样就完成了DataBinding的数据绑定,现在运行你应用程序就可以得到你设置的结果。
DataBind + 事件
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <import type="com.example.peiyu_wang.databinding.entity.StudentObserve" /> <import type="android.view.View.OnClickListener" /> <variable name="stu" type="StudentObserve" /> <variable name="clicklistener" type="OnClickListener" /> </data> <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.peiyu_wang.databinding.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@{stu.name}" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="@{stu.address}" android:textSize="16sp" /> <!--事件绑定--> <Button android:id="@+id/change" android:layout_width="match_parent" android:layout_height="50dp" android:onClick="@{clicklistener::onClick}" android:text="change Text" /> </LinearLayout></layout>
可以看到xml中定义类Button的点击事件,。其实就是调用类clicklistener.onClick(View v)方法,此时不一定需要时View.OnClickListener
BindingAdapter
android:text=”@{user.firstName}”这种情况下
DataBinding框架的处理过程分成三步
1.对binding表达式求值
2.寻找合适的BindingAdapter,如果找到,就调用它的方法
3.如果没有找到合适的BindingAdapter,就在View上寻找合适的方法调用
因此默认情况下,会调用android命名空间下的View.setText进行更新数据,那么此时我想进行这样的一个操作,当TextView里面的内容没有变化的时候,就不进行更新,更新也一样嘛。因此我们重新设置我们自己的BindingAdapter,此时在User中定义自己的处理android:text的处理方法
@BindingAdapter("android:text") public static void setText(TextView view, CharSequence text) { final CharSequence oldText = view.getText(); if (text == oldText || (text == null && oldText.length() == 0)) { return; } if (text instanceof Spanned) { if (text.equals(oldText)) { return; // No change in the spans, so don't set anything. } } else if (!haveContentsChanged(text, oldText)) { return; // No content changes, so don't set anything. } view.setText(text); }
DataBinding UI自动更新数据
ActivityMainBinding.invalidateAll()
此时的数据对象,无需其他修饰.如:
public class Student { private String name; private String address; private String company; public Student() { } public Student(String name, String address, String company) { this.name = name; this.address = address; this.company = company; } public String getName() { return name; } public String getAddress() { return address; } public String getCompany() { return company; } public void setName(String name) { this.name = name; } public void setAddress(String address) { this.address = address; } public void setCompany(String company) { this.company = company; }}
在改变数据后,调用对应ViewDataBinding子类的invalidateAll()方法。此时需要手动调用刷新
将数据类继承自android.databinding.BaseObservable
public class StudentObserve extends BaseObservable { private String name; private String address; private String phone; public StudentObserve() { } public StudentObserve(String name, String address, String phone) { this.name = name; this.address = address; this.phone = phone; } @Bindable public String getName() { return name; } @Bindable public String getAddress() { return address; } @Bindable public String getPhone() { return phone; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } public void setAddress(String address) { this.address = address; notifyPropertyChanged(BR.address); } public void setPhone(String phone) { this.phone = phone; notifyPropertyChanged(BR.phone); }}
数据会变化的字段用@Bindable注解修饰getXXX方法,点击build->makeProject后,会自动在BR类中添加这个会修改的字段,使用常量表示,在setXXX方法中,表示要更新的字段传入。这样指定更新某个部分,而不是全部都更新,因此避免了数据没有改变那部分也重新赋值的消耗,性能更好。可以想想为什么@Bindable要修饰在getXXX方法而不是setXXX方法?可以根据android:text=”@{user.firstName}”,这里写的是user.firstName,其实是调用了user.getFirstName方法进行猜想,UI数据的来源是getXXX方法,这些数据是要更新的,因此我们在数据Model中表示要更新的部分,就是用@Bindable修饰getXXX方法。可以有点难理解。
使用ObservableField
有这么一种情况,如果一个数据类只有一个或者两个字段需要更新的,而且继承BaseObservable,显然没有必要,因此Google针对单个成员变量定义的Observe–BaseObservable.
public ObservableField<String> name = new ObservableField<String>();public ObservableField<String> address=new ObservableField<String>();public ObservableField<String> phone=new ObservableField<String>();
后两种只需要修改数据,UI就会自动更新。不需要像第一种那样需要手动调用对应ViewDataBinding子类的invalidateAll()方法.
DataBinding + RecycleView
1.原本Holder持有View对象,因为使用了DataBinding,所有不需要持有View,只需要持有ViewDataBinding对象
public class BaseViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder { private T dataBing; public BaseViewHolder(View itemView) { super(itemView); } public T getDataBing() { return dataBing; } public void setDataBing(T dataBing) { this.dataBing = dataBing; }}
RecycleViewAdapter
public class BaseRecycleViewAdapter<T, K extends ViewDataBinding> extends RecyclerView.Adapter<BaseViewHolder<K>> { protected List<T> lists; //数据源 protected int resouceId; //布局ID protected int variableId; //布局内VariableId,就是使用BR类自动生成的常量int,只想layout中使用的data变量 public BaseRecycleViewAdapter(List<T> lists, int resouceId, int variableId) { this.lists = lists; this.resouceId = resouceId; this.variableId = variableId; } /** * 创建绑定数据的ViewHolder(实际上就相当于初始化出来界面) * * @param parent * @param viewType * @return */ @Override public BaseViewHolder<K> onCreateViewHolder(ViewGroup parent, int viewType) { K itemBing = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), resouceId, parent, false); //获取DataBing相当于获取View BaseViewHolder<K> holder = new BaseViewHolder<K>(itemBing.getRoot());//初始化ViewHolder存放View holder.setDataBing(itemBing); return holder; } @Override public void onBindViewHolder(final BaseViewHolder<K> holder, final int position) { T data = lists.get(position);//获取数据 holder.getDataBing().setVariable(variableId, data);//赋值 if (listener != null) {//设置单击事件 holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { listener.onItemClick(holder.getDataBing(), position); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { listener.onLongItemClick(holder.getDataBing(), position); return false; } }); } holder.getDataBing().executePendingBindings();//刷新界面 } @Override public int getItemCount() { return lists == null ? 0 : lists.size(); } //自定义item单击事件 protected OnItemClickListener listener; public void setListener(OnItemClickListener listener) { this.listener = listener; } public interface OnItemClickListener { public void onItemClick(ViewDataBinding dataBinding, int position); public void onLongItemClick(ViewDataBinding dataBinding, int position); }}
参考资料
http://www.jianshu.com/p/9fb720f405a7
http://www.jianshu.com/p/7c8b484cda91
http://www.jianshu.com/p/686bfc58bbb0
http://www.jianshu.com/p/de4d50b88437
http://www.jianshu.com/p/ad170ed79324
- DataBinding详解
- android dataBinding详解
- android dataBinding详解
- DataBinding使用教程详解
- Android DataBinding 详解
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- DataBinding
- databinding
- Android databinding详解(一)--layout解析
- Android databinding详解(二)--activity解析
- Android databinding(详解三)--自定义属性使用
- Canvas画布
- 北京信息学培训第三天
- 【C/C++】类中的const成员 static成员
- 读书笔记:《产业阴谋链》
- vue+vueRouter+vuex+vux(微信项目开发框架)
- DataBinding详解
- loopback 地址
- java垃圾回收机制--CMS
- Linux centOS6.9关闭防火墙
- 面试心得 --- 晶朝科技有限公司面试心得
- uva 12657 Boxes in a Line
- 对于仍在 初学 还不会 C 的的 同学......链接:......教程:......
- 动态修改strings.xml中的值及获取app版本号
- 关于IO流在复制文件时出现java.io.FileNotFoundException: D:\xxx (拒绝访问。) 拒绝访问的问题