最近在学习DataBinding的使用,中间遇到了不少的坑,记录以下,帮助以后学习DataBinding的朋友。
一:引入
其实,我们只需要在对应的Module的build.gradle中添加这么一句话即可。
dataBinding {
enabled=true
}
不需要加别的东西。
二:简单使用的时候我出现的错误
1.比如这么写
android:text="@{User.name}"/>
那么,我User中的成员变量name,必须是String类型的。因为DataBinding不是使用反射的,你必须公有化才能访问得到。否则会报找不到类,···javac什么的异常。
2.比如我这么写
android:text="@{User.age}
那么,我的age这个成员变量必须是String类型的,如果使用int类型的话,程序会crash掉
但如果必须是int类型,那么该怎么写呢,可以这么写
android:text="@{String.valueOf(user.age)}"
因为这个android:text 中是 String 类型
3.比如我这么写
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
ActivityMainBinding这个类又是来自于哪里?他是根据R.layout.activity_main来生成的。如果我的布局文件是main_activity,那么就应该生成MainActivityBinding。
三:开始使用
引入DataBinding的时候我遇到过以上的错误,之后就让我们一起学习DataBinding的使用吧。
我推荐大家把这个Demo下载下来,跟着源码去读一读,感受一下功能和写法
DataBindingDemo
而今天,我们就是去Read The Fucking Source Code。
首页是这样的,我们一个一个进去看
1:A SIMPLE BASIC EXAMPLE
这个比较简单,建议读者自己能跟着代码走一遍,有什么不懂得可以留言,我们一起探讨进步。
说几个需要注意的地方
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"//支持三元运算符。需要导入View包
android:text="@{String.valueOf(user.age)}"//前面说了,@{只能是String},为什么,可以看3.1,匹配规则
android:text="@{StringUtils.capitalize(user.firstName)}"//前面导入了这个包,可以调用这个静态方法
android:text="@{user.displayName ?? user.lastName}"//它表达的是如果左边不是 null 的,那么使用左边的值,否者使用右边的值。在棉花糖的文章里有介绍
2:CustomBinding
这个的重点在这里
ContractBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_custom_binding);
看到了没,xml是activity_custom_binding,但是这个类却是ContractBinding
他是在这里声明过了的
data class=".ContractBinding"//除了使用框架自动ActivityCustomBinding,我们也可以通过这种方式自定义类名
3:INCLUDES
从这里开始要说的东西就多了。。。。建议刚入门的读者能耐心的看完
3.1万物皆对象
不知道读者有没有想过,为什么binding这个对象会有binding.setUser()这个方法呢?而我们当前看的这个Activity为什么会有
binding.setListener(this);
binding.setOkText("to toast");
这两个方法呢?
因为我们在data中声明过这些东西
<data>
<import type="com.liangfeizc.databinding.model.User" />
<variable
name="user"
type="User" />
<variable
name="listener"
type="com.liangfeizc.databinding.listener.OkListener" />
<variable
name="okText"
type="String" />
</data>
name中标识的user,listener,okText,就是binding所具有的属性,而属性就应该具有set.get方法,这就是万物皆对象,而后面的type,就是属性名的类型。这么说理解吗?很简单,把Binding当做一个对象,万物迎刃而解。
还有一处用到这个思想,问题迎刃而解,我们看include这个布局,有三个自定义的命名空间:
bind:user="@{user}"
bind:okText="@{okText}"
bind:listener="@{listener}"
这就是给他赋值的。这里,我推荐大家看一下https://realm.io/cn/news/data-binding-android-boyar-mount/
棉花糖的对自动属性的介绍。然后我说一下我的理解。
<variable
name="user"
type="com.liangfeizc.databinding.model.User" />
首先声明这一个变量,让我们的Binding对象具备了
private User user;
这一个属性,而我在布局文件中,可以任意的使用我自己的成员变量user。
这个时候,我可以自定义一个命名空间,并给我的任意控件添加任意属性
比如说
<include
layout="@layout/layout_btn_ok"
bind:okText="@{okText}"
bind:listener="@{listener}"/>
<include>这个标签怎么会有这两个属性,毫无疑问是自动加上去的。那么它又该如何理解呢?
我们使用了 bind:okText这个属性,但实际上该标签并没有提供这么一个 xml 布局属性。这个时候我们就可以想一想自动绑定。xml属性不也是对象的一个属性吗?既然是属性,那么就有set,get。那么我们可以这么想,
private String okText;//如果我后面的@{okText},就是String类型的我就会自动匹配过去。
private OkListener listener;//如果我后面的@{listener},就是OkListener类型的我也会自动匹配过去。
不知道这么说大家有没有理解。这里不好想,一定要自己多看几遍代码,多思考一下。慢慢来。没必要急躁。
3.2···id的作用
不知道大家有没有为这种写法而感到诧异
binding.layoutInput.etName.addTextChangedListener(null);
我去这是什么啊!~这是id。我们可以ctrl点进去看一看。
android:id="@+id/layout_input"
android:id="@+id/et_name"
只要给 View 定义一个 ID,Data Binding 就会为我们生成一个对应的 final变量。以_为单词的分隔符,命名符合java规范。
3.3 点击事件
如果大家看过棉花糖的教程,就会看,这是什么啊,就不能说的细一点吗?
<Button android:onClick="clicked" …/>
<Button android:onClick="@{handlers.clicked}" …/>
<Button android:onClick="@{isAdult ? handlers.adultClick :handlers.childClick}" …/>
<Button android:onTextChanged="@{handlers.textChanged}" …/>
其实不难想。第一个就是我们平常最常用的点击事件的写法,和databinding没有关系啊。
剩下的都是一个模子出来的。
官方的建议是将事件都写到一起。
就比如代码的作者是这么写的
public interface OkListener {
void onClickOk(View view);}
然后让
android:onClick="@{listener.onClickOk}"
在执行时候就和我们平时使用差不多了
binding.setListener(this);
就这几步,让你省去了fbc的烦恼。不过,我宁愿想给每一个按钮写一个ID,我也不想给每一个按钮写一个方法名!!!
3.3 include
其实说了那么多,到现在才进入正题。不知道您又学会了多少,学习千万别急,慢慢来,别人的东西只能给你说以下有这么一个东西,具体的还是需要自己多思考的。
闲话:曾经我一直觉得,啊,我会这么多三方库,什么功能都能实现,我要上天了。虽说君子善假于物也,但轮子谁都会滚,却不是我这样的坐井观天,沾沾自喜,仅会简单的使用。我现在想要改变这种状况,尝试着去读各种demo的源码,有时候也是看的头蒙蒙的,感觉会的越来越少,也有些急躁,一旦急躁什么东西都是学不下去的····难受极了。不过,我现在放正了心态,慢慢来学,缓慢而不懈怠,总有一天,一通百通。共勉,加油,我们是第一,我们是最棒的!!!
说了这么多闲话,其实是为了掩饰我内心的恐惧,因为我根本不知道,为什么要在include引用的布局内再写一遍data啊!
我是这么猜测的:
数据绑定,一个大布局对应一个数据源。比如说我这个数据源有5份数据,我这个大布局中有三个小布局,第一个小布局需要1份数据,第二个需要两份数据,第三个需要5份数据。我可以按照这三个小布局声明的需求来去给他们分配。而分配这个动词,就对应了3.1中的自动属性来解决,就是填充!
而且可以保证数据源相对于这个布局的唯一。
我是这么猜的不知道你们那。
include就说这么多,还需要再看,这一个代码写的很好看。
4.Collection
这里讲的是集合作为数据源的使用,集合作为数据源用到的地方还是蛮多的。
4.1需要注意的地方
java.lang包下的类,如String什么的,在data中是不需要导包的。
需要用转义符& lt;来代替<,
4.2使用
其实有了上面的基础,在这里学习就很简单了,虽然代码写的很吓人,各种转义符,但是其实就是一点,单列集合可以通过索引找到值,双列集合可以通过key找到对应的值。
有些朋友可能被SparseArray吓到了,其实他就是HashMap<Integer,?>的改良版。想了解的朋友可以看一下这个链接http://www.cnblogs.com/RGogoing/p/5095168.html
5.Resource
官方是这样介绍资源内容的:
我们希望你能在你的表达式中使用资源引用内容,因此你可以在你的表达式中使用资源和字符串格式化方法。
其实这里也很简单我们慢慢来看。
在表达式中引用资源↓
android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"
内联字符串格式:
android:text="@{@string/nameFormat(firstName, lastName)}"
同时,在string.xml文件中,这些只是占位符,s是字符串,d是整数
<string name="nameFormat">Full Name: %1$s:%2$s</string>
<string name="nameFormatWithAge">Hello %1$s %2$s, %3$d ages</string>
内联复数格式:这个我是真没用过,网上资料也少,不做解释···
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
<plurals name="orange">
<item quantity="one">Have an orange</item>
<item quantity="other">Have %d oranges</item>
</plurals>
6.Observable
这个我说不真切,对他也心存疑惑,还有貌似现在已经支持双向绑定了。等我会了再来写····
7.ViewWithIds
这个之前已经说过了,这里就不说了。
android:id="@+id/firstName"===>binding.firstName.
8.ViewStub
ViewStub是Android布局优化的一种方式,是一个非常轻量的View,他和View.Gone,View.Visable类似,但又不一样,可以把它当成一个占位符,只会被加载一次,是一个非常不错的控件。
在这里,他和上面讲过的include在具体的实现上类似,因为它本身也少包裹着一层布局的。
但是奇怪的是
//这里标红,不能自动提示,虽然报错,但能运行,应该是studio支持的不好
if (!mBinding.viewStub.isInflated()) {
mBinding.viewStub.getViewStub().inflate();}
9.Dynamic
重点来了。
RecyclerView + DataBinding.让我们把之前学习的运用过来,最好先自己试着写一下,再来看。
先说一下这个方法
setHasFixedSize(true);//我查了一下,貌似是说size固定的话,使用此方法,效率会提升
我们重点看一下Adapter
public static class UserHolder extends RecyclerView.ViewHolder {
final UserItemBinding mBinding;
//通过构造将View视图和DataBinding进行绑定
public UserHolder(View itemView) {
super(itemView);
mBinding = DataBindingUtil.bind(itemView);
}
//其实这个方法的本质只是对外暴露UserItemBinding 用的
public void bind(@NonNull User user) {
mBinding.setUser(user);
}
}
在看一下这俩方法
@Override
//通过打气筒获取到item_view,同时传递给ViewHolder
public UserHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.user_item, viewGroup, false);
return new UserHolder(itemView);
}
@Override
//通过Binding对象将数据和视图进行绑定
public void onBindViewHolder(UserHolder holder, int position) {
holder.bind(mUsers.get(position));}
我去,这也太简单太方便太简单了!!!
不过,平时我使用的都是陈宇明大神的https://github.com/CymChad/BaseRecyclerViewAdapterHelper
也是超级好用,超级方便。先学习,以后会有更简单的东西的··
10.Attribute setters
这个之前我们在3.1的时候也说过,可能我说的不是很明白,这里有例子,我把作者的话引入过来。
其实看这一块的时候,我一直惊呼,我靠太牛逼了。。。怪我没出息。
有了 Data Binding,即使属性没有在 declare-styleable
中定义,我们也可以通过 xml 进行赋值操作。 为了演示这个功能,我自定义了一个 View ,属性资源 [R.styleable.NameCard] 中只定义了一个 age
属性,其中 firstName 和lastName只有对应的两个 setter方法。只要有 setter方法就可以像下面代码一样赋值:
<com.liangfeizc.databindingsamples.attributesetters.UserView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/largePadding"
app:onClickListener="@{activity.clickListener}"
app:firstName="@{@string/firstName}"
app:lastName="@{@string/lastName}"
app:age="27"
/>
onClickListener
也是同样道理,只不过我们是在 Activity 中定义了一个 Listener
意思其实和我差不多,我这个标签没有这个属性,我可以自定义强塞给它这个属性,当它拿到属性之后怎么去处理,一般的话会引入一个注解,比如我们这里的@BindingAdapter({"imageUrl", "error"})
这是什么意思类?一开始我也不理解,后来看了11中的这个方法
@BindingAdapter("layout_height")
public static void setLayoutHeight(View view, float height) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = (int) height;
view.setLayoutParams(params);
}
那么意思再也明显不过了,我得到的这个值应该怎么去处理它。第一个参数对应的是这个View控件,而第二个就是我们给他 赋的值
11.Conversions,终于要完了
不好意思,这个我也没看懂···
··综上,也就辣么多了。其实我唯一能告诉大家的就是,DataBinding真的很简单也很方便使用,你需要做的仅仅是看一遍demo,不过最重要的还是千万别急躁,啥事都有个循序渐进的过程,尤其是学习。加油。