前言
记得应该是在去年,头一次接触到Data Binding这个家伙,内容就感到诧异,这么6的家伙,肿么才知道???
不过,可能是时间紧,项目忙,也一直没能好好学习了解下,更别提真正应用了。
还记得,当年的黄油刀,一经使用,便再也停不下来,当然,针对黄油刀,LZ也专门写了篇博文,有兴趣的可以查看:
无奈的是,黄油刀,也不过是在LZ重写的一个项目中玩的嗨起,而其余的项目,也是依旧老套路,坑多的都不想填了。。。
前几天,和LZ老伙计聊起来,老伙计对这个Data Binding可谓赞不绝口,而LZ的态度如下:
随着越听越不对劲,LZ突然感觉,我插~
所以,嘿嘿嘿,就有了今天这篇文章~~~
本文目标
基于Data Binding官方文档以及相对应初级到进阶的实例,让你看完本篇即可短时间内掌握Data Binding使用~
关于技术选择
这里LZ插播一条关于个人想法。
关于技术的选择,其实,不必说,每个人都有每个人的选择。
好比当年的Eclipse与现在的Android Studio想比,虽说死丢丢现在屌炸天,但是你也不可能否认当前Eclipse陪伴的岁月,更何况,Eclipse陪伴多少人的Android入门~~~
有人说,新技术虽说屌,但是可行性,稳定性没个保障,好比14年时,培训期间使用SSH,可真正面试的时候,公司大部分使用的Struts 1,在虚心请教之后,才了解到Struts 2拥有一个漏洞,但是也不可因为这个去说Struts 2并不出色。
同样,新技术的出现,可谓代表新的一波技术到来,同样我们可以在到来的过程中,学习新技术的思想,从而加强我们底层的韵味~~~
所以,新技术有新技术的爽,老技术有老技术的韵味,而关于如何抉择,还是要看大家~~~
不过,多了解,总归没有坏处,思维的拓宽所带来的益处可不是一丁半点~~~
在此引用大牛一句话:
任何一种技术的出现都是为了解决问题的,而不是为了创造出其他的问题,或者在解决一个问题的同时创造了另外的问题。
用什么技术不是目的,解决问题才是目的。
Data Binding简介
Data Binding,出来也有几年了,随着Beta版本到Release版本,直到现在,Data Binding确实为我们带来了很多让人眼前一亮的东西,But,郁闷的是,LZ如今才是真正知道从而学习,并且希望能在项目中真正应用。
希望在本文结束后,我和各位小伙伴能对此神器爱不释手~~~
首先来说,Data Binding是个什么鬼?鉴名其意,数据绑定,是谷歌对MVVM(Model-View-ViewModel)在我们Android上贴心的实现~~~
优势
效率(性能)高。无侵入式,无反射;
节省大量代码,提高开发效率。例如频繁出现的findViewById;
功能强大,支持内容较为广泛,这点还是有待挖掘啊~
劣势
增加编译出的 apk 文件的类数量和方法数量;
。。。貌似还有点,但是问题不大。。。
使用前提了解
生成规则
Binding工具类生成规则:
默认以布局命名规则生成activity_main.xml,例如布局文件名为:activity_main,则对应生成则为:ActivityMainBinding。控件ID生成规则:
默认已驼峰命名法,例如控件ID为:user_id,则后续使用binding实例化点userId即可。
使用
使用之前,需要在Android Studio中build.gradle配置开启Data Binding,具体如下:
dataBinding { enabled = true }
一、改造布局,使其支持Data Binding
Android Studio 3.0 之后默认布局如下:
<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"></android.support.constraint.ConstraintLayout>
而我们只需要将layout标签替换为根布局标签即可,剩下保持不变,如下:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.constraint.ConstraintLayout></layout>
Ok,到此完成改造第一步,下面进行具体操作过程:
二、数据绑定
一起来看下效果:
二一: Activity中更新UI
编写布局:
<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 --><layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text="Activity中更新UI" android:textSize="16sp" /> <EditText android:id="@+id/userName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入姓名" /> <EditText android:id="@+id/userAge" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入年龄" /> <TextView android:id="@+id/userAddress" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" android:textColor="#fff" android:textSize="16sp" /> </LinearLayout></layout>
Activity设置:
// 实例化布局 初始化Binding ActivityBasisUsageBinding basisUsageBinding = DataBindingUtil.setContentView(this, R.layout.activity_basis_usage); // 设置值 basisUsageBinding.userName.setText("静心Study"); basisUsageBinding.userAge.setText("22"); basisUsageBinding.userAddress.setText("大好河山,张家口");
显示结果为:
这里大家注意下:DataBindingUtil.setContentView(this, R.layout.activity_basis_usage);
我们通过拿到这个实例去操作后面的一些操作,具体关键待下一篇从源码角度分析查看。
二二: XML中直接更新UI
这里需要注意:
Data Binding中引入了命名空间
具体对应的操作应为:使用< data >块,假设现在你想使用UserBean,则需要使用< variable >标签引入UserBean实体即可,具体操作如下:
改造布局:
<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 --><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.hlq.databindingdemo.bean.UserBean" /> </data> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text="Activity中更新UI" android:textSize="16sp" /> <EditText android:id="@+id/userName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入姓名" /> <EditText android:id="@+id/userAge" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入年龄" /> <TextView android:id="@+id/userAddress" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" android:textColor="#fff" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text="XML中直接更新UI" android:textSize="16sp" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入姓名" android:text="@{user.userName}" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="请输入年龄" android:text="@{user.userAge}" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="15dp" android:text='@{"地址:"+user.userAddress}' android:textColor="#fff" android:textSize="16sp" /> </LinearLayout></layout>
Activity中初始化实体类:
UserBean userBean = new UserBean("HLQ_Struggle", "XML绑定年龄:22", "XML直接绑定地址"); basisUsageBinding.setUser(userBean);
显示结果为:
二三: 参数为空处理
这里不得不提一嘴,还记得Android中接收后台返回实体类,多少难免会有一些空值,而我们在不注意的时候直接使用则会引发空指针这个异常,那么使用Data Binding后,我们则无需关注这块内容。
这时候小伙伴就会说了,那如果我的实体压根就是空,直接使用不会异常么?
如果使用Data Binding,如果数据出现空时,则默认显示当前类型的默认值。
引入布局:
<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 --><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="stu" type="com.hlq.databindingdemo.bean.StudentBean" /> </data> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text="参数为空处理" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text='@{"String参数为空:"+stu.stuName}' android:textColor="#fff" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text='@{"int参数为空:"+stu.stuAge}' android:textColor="#fff" android:textSize="16sp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="15dp" android:text='@{"boolean参数为空:"+stu.stuIsPer}' android:textColor="#fff" android:textSize="16sp" /> </LinearLayout></layout>
Activity直接设置值:
basisUsageBinding.setStu(new StudentBean());
显示结果为:
三、设置事件
当配合使用Data Binding后,我们原有的一些设置事件与我们之前的使用方式有有一些出入,具体如下:
首先需要定义一个Presenter内部类(当然,如果你想抽取出去也无可厚非);
接着,bind提供我们如下几种方式使用事件:
直接绑定内部方法;
Lambda 表达式。
复写原有系统提供事件,例如:onClick等;
最后,我们需要给Bind设置setPersenter或者setVariable。
上面简单列举了下理论,下面着重说明一些关键:
首先需要在XML中引入命名空间,也就是我们定义的事件内部类:
<variable name="persenter" type="com.hlq.databindingdemo.activity.BindListenerActivity.Presenter" />
其中,调用事件的方式有俩种,分别如下:
android:onClick="@{persenter.onClick}"
android:onClick="@{persenter::onClick}" 官方更为推崇使用这种。
而Lambda 表达式使用方式如下:
android:onClick="@{() -> persenter.getUserClick()}"
android:onClick="@{() -> persenter.showUserName(user)}"
而最后,关于我们初始化我们这个事件内部类,方式同样有俩种,如下:
mBindListenerBinding.setPersenter(new Presenter());
mBindListenerBinding.setVariable(BR.persenter,new Persenter());
那么到此,开始我们愉快的撸码之旅吧~
首先,在我们的Activity中定义事件类以及进行初始化:
package com.hlq.databindingdemo.activity;import android.databinding.DataBindingUtil;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Toast;import com.hlq.databindingdemo.R;import com.hlq.databindingdemo.bean.UserBean;import com.hlq.databindingdemo.databinding.ActivityBindListenerBinding;/** * author : HLQ * e-mail : 925954424@qq.com * time : 2018/02/13 * desc : 事件绑定 * version: 1.0 */public class BindListenerActivity extends AppCompatActivity { private BindListenerActivity selfActivity = BindListenerActivity.this; private ActivityBindListenerBinding mBindListenerBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBindListenerBinding = DataBindingUtil.setContentView(this, R.layout.activity_bind_listener); mBindListenerBinding.setPersenter(new Presenter());// mBindListenerBinding.setVariable(BR.persenter,new Persenter()); mBindListenerBinding.setUser(new UserBean("贺大大", "22", "China")); } public class Presenter { public void onClick(View view) { Toast.makeText(selfActivity, "点到了哦~", Toast.LENGTH_SHORT).show(); } public void getUserClick() { Toast.makeText(selfActivity, "有人在呼唤你~", Toast.LENGTH_SHORT).show(); } public void showUserName(UserBean userBean) { Toast.makeText(selfActivity, "看看是谁:" + userBean.getUserName(), Toast.LENGTH_SHORT).show(); } } }
接着,布局文件引入命名空间,进行相关调用:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.hlq.databindingdemo.bean.UserBean" /> <variable name="persenter" type="com.hlq.databindingdemo.activity.BindListenerActivity.Presenter" /> </data> <android.support.v7.widget.LinearLayoutCompat xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="15dp" tools:context="com.hlq.databindingdemo.activity.BindListenerActivity"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{persenter.onClick}" android:text="测试监听器绑定." /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{persenter::onClick}" android:text="测试监听器绑定:" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{() -> persenter.getUserClick()}" android:text="测试方法绑定" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{() -> persenter.showUserName(user)}" android:text="测试方法绑定传值" /> </android.support.v7.widget.LinearLayoutCompat></layout>
最后,一起来看结果如何?
四、一起来探表达式
表达式,在我们的Data Binding中也有所体现,下面简单描述一些常用的表达式。
表达式 | Desc |
---|---|
< > = | 小于 大于 等于 |
? : | 三元运算符 |
?? | 三元运算符 |
首先,我们实例化数据源:
ActivityExpressionBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_expression); binding.setUser(new UserBean("贺大大", "22", "河北", 0));
接下来,我们开始编写xml玩物:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="user" type="com.hlq.databindingdemo.bean.UserBean" /> <variable name="view" type="android.view.View" /> </data> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.hlq.databindingdemo.activity.ExpressionActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView style="@style/contentStyle" android:text="一起来探表达式" /> <TextView style="@style/titleStyle" android:text="模拟数据:姓名:贺大大 年龄:22 地址:河北 个数:0" /> <TextView style="@style/titleStyle" android:text="一、三元运算符(用户名不等于空则正常显示,反之显示贺大宝)" /> <TextView style="@style/contentStyle" android:text='@{user.userName!=null?user.userName:"贺大宝"}' /> <TextView style="@style/titleStyle" android:text="二、三元运算符替代者(相比第一条,简单省事儿)" /> <TextView style="@style/contentStyle" android:text='@{user.userName??"贺大宝"}' /> <TextView style="@style/titleStyle" android:text="三、比较(例如比较个数等于0显示反之隐藏)" /> <TextView style="@style/contentStyle" android:text="看看我是被隐藏还是显示呢" android:visibility="@{user.userCount==0?view.VISIBLE:view.GONE}" /> </LinearLayout> </ScrollView></layout>
这里需要注意一点,Data Binding中如果使用View,需要将View引入一下,这里作用类似引入命名空间一样。
<variable name="view" type="android.view.View" />
下面我们一起来看下效果:
五、ViewStub Study
ViewStub,这个小神器,这个在Data Binding也有所变换,如下:
首先编写一个简单的layout:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView style="@style/contentStyle" android:text="今天上班了" /> <TextView style="@style/titleStyle" android:text="难受想哭,心里默默MMP" /></LinearLayout>
效果如下:
编写布局:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hlq.databindingdemo.activity.ViewStubActivity"> <TextView style="@style/contentStyle" android:text="ViewStub示例" /> <ViewStub android:id="@+id/viewStub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/layout_view_stub" /> </LinearLayout></layout>
Activity进行初始化:
ActivityViewStubBinding viewStubBinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub); viewStubBinding.viewStub.getViewStub().inflate();
一起来看效果:
六、include Study
include,使用时,与上面ViewStub引用类似,那么如果现在需要给include内容设置数据,又该如何操作呢?
简单过一遍代码,之后进行简单总结。
首先,编写一个layout,由于我们需要将数据源也传递过去,所以这里需要提前声明下实体。
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="com.hlq.databindingdemo.bean.UserBean" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView style="@style/titleStyle" android:text='@{"姓名:"+user.userName}' /> <TextView style="@style/titleStyle" android:text='@{"年龄:"+user.userAge}' /> <TextView style="@style/titleStyle" android:text='@{"地址:"+user.userAddress}' /> </LinearLayout></layout>
Activity中初始化数据源:
ActivityIncludeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_include); binding.setUser(new UserBean("静心Study", "22", "帝都"));
在所对应的Activity layout中通过bind绑定数据源即可:
<?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" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="user" type="com.hlq.databindingdemo.bean.UserBean" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.hlq.databindingdemo.activity.IncludeActivity"> <TextView style="@style/contentStyle" android:text="include引用布局示例" /> <include layout="@layout/include_item_layout" bind:user="@{user}" /> </LinearLayout></layout>
那么我们来稍稍的总结一下吧:
首先,使用时,需要引用对应的实体类,也就是引入命名空间。其次,如果如有include内容需要传递数据源时,那么这个时候仅仅需要在include标签通过使用bind:数据源别名="@{数据源别名}"即可,那么如果需要多个include时,同理即可,下面举个例子:
<include layout="@layout/include_item_layout" bind:user="@{user}" bind:bean="@{bean}"/>
实例化如下:
ActivityIncludeBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_include); binding.setUser(new UserBean("静心Study", "22", "帝都")); binding.setBean(new ClassBean("哇哈哈"));
效果如下:
到此为止,Data Binding基础篇截稿~