this.mboundView1.setGravity(android.view.Gravity.CENTER)
因为TextView中存在目标对象setGravity()
且参数类型和返回值类型都一样,所以生成的代码没有任何问题。
但是如果找不到对应的set方法,那么编译器就会报错,告诉我们在某个View中找不到相关方法,举个官网上的例子:android:tint
属性实际对应的方法为setImageTintList()
,而不是setTint()
,先看如下代码:
<ImageView
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="center"
android:text="BindingMethodsDemo02"
android:tint="@{@color/colorAccent}"/>
可以看到,在android:tint
属性中,我们使用了dataBinding表达式,如果不做任何处理,那么dataBinding在编译时就会报错,因为在ImageView中找不到对应的setTint()方法,android:tint
属性对应的应该是setImageTintList()
,所以为了让程序能正常运行,我们需要让dataBinding能寻找指定的set方法来生成代码,即使用setImageTintList()
方法而不是setTint()
方法;而@BindingMethod与@BindingMethods就是用来指定某个xml属性对应的set方法的。这也就是@BindingMethod与@BindingMethods注解存在的意义。
ps:可能会有人说,我没有做任何处理,直接使用android:tint="@{@color/colorAccent}"
也没有报错啊,其实是因为dataBinding已经帮我们定义好了转换方法了,在你使用android:tint="@{@color/colorAccent}"
时,已经帮你自动使用setImageTintList()
方法生成代码了。
说完了@BindingMethod与@BindingMethods存在的意义,那么@BindingMethod与@BindingMethods到底怎么使用呢?
@BindingMethod
有3个字段,这3个字段都是必填项,少一个都不行:
- type:要操作的属性属于哪个View类,类型为class对象,比如:ImageView.class
- attribute:xml属性,类型为String ,比如:”android:tint”
- method:指定xml属性对应的set方法,类型为String,比如:”setImageTintList”
组合起来如下:
@BindingMethod(type = ImageView.class, attribute = "android:tint", method = "setImageTintList")
作用就是:在为ImageView的android:tint属性生成代码时,使用setImageTintList()方法生成。
@BindingMethods
因为@BindingMethod注解不能单独使用,必须要结合@BindingMethods才能发挥其功效,所以@BindingMethods注解其实就是一个容器,它内部有一个BindingMethod数组,存放的是一个一个的BindingMethod。
@BindingMethod与@BindingMethods结合起来使用的代码示例如下:
@BindingMethods({
@BindingMethod(type = android.widget.ImageView.class, attribute = "android:tint", method = "setImageTintList"),
@BindingMethod(type = android.widget.ImageView.class, attribute = "android:tintMode", method = "setImageTintMode"),
public class ImageViewBindingAdapter {}
这样就完了吗?对,没错, @BindingMethod与@BindingMethods就是这样用的,是不是很简单?定义完以上的代码之后,在编译时,如果你使用到了上面指定的某个属性,且在属性值中使用了dataBinding表达式,那么在生成代码时,dataBinding会自动选择指定的方法去生成。
ImageViewBindingAdapter路径为:android.databinding.adapters.ImageViewBindingAdapter
5.@Bindable
- 该注解用于双向绑定,需要与 notifyPropertyChanged()方法结合使用
- 该注解用于标记实体类中的get方法或“is”开头的方法,且实体类必须继承BaseObserable.
- 用法极其简单
实体类也可以不用继承BaseObservable,而是实现Observable接口,但是需要自行处理一些接口方法逻辑,BaseObservable是实现Observable接口的类,内部已经做好了相关逻辑处理,所以选择继承BaseObservable相对简单一些。
public class User extends BaseObservable {
private String name;
private int age;
private String sex;
private boolean isStudent;
@Bindable
public String getName() {
return name;
public void setName(String name) {
this.name = name;
notifyPropertyChanged(com.qiangxi.databindingdemo.BR.name);
@Bindable
public int getAge() {
return age;
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(com.qiangxi.databindingdemo.BR.age);
@Bindable
public String getSex() {
return sex;
public void setSex(String sex) {
this.sex = sex;
notifyPropertyChanged(com.qiangxi.databindingdemo.BR.sex);
@Bindable
public boolean isStudent() {
return isStudent;
public void setStudent(boolean student) {
isStudent = student;
notifyPropertyChanged(com.qiangxi.databindingdemo.BR.student);
@Bindable({"name", "age", "sex", "isStudent"})
public String getAll() {
return "姓名:" + name + ",年龄=" + age + ",性别:" + sex + ",是不是学生=" + isStudent;
既然用法如此简单,那就不多说了。
@Bindable注解是用来干什么的?
使用@Bindable注解标记的get方法,在编译时,会在BR类中生成对应的字段,然后与notifyPropertyChanged()方法配合使用,当该字段中的数据被修改时,dataBinding会自动刷新对应view的数据,而不用我们在拿到新数据后重新把数据在setText()一遍,就凭这一点,dataBinding就可以简化大量的代码.具体在代码中如何使用双向绑定,在下一篇文章【DataBinding使用教程(四):BaseObservable与双向绑定】会有详细的使用示例,这里就不赘述了。
6.@BindingAdapter
- 用于标记方法,方法必须为公共静态方法
- 方法的第一个参数的类型必须为View类型,不然报错
- 用来自定义view的任意属性
@Target(ElementType.METHOD)
public @interface BindingAdapter {
String[] value();
boolean requireAll() default true;
上面是源码中@BindingAdapter
注解的定义,可以看到:
- value属性是一个String数组,用来存放自定义的属性,示例:android:onItemClick
,app:onItemClick
- requireAll是一个布尔值,用来表示定义的所有属性是否必须都要使用。
完整示例:
@BindingAdapter(value = {"android:onItemClick", "android:onLoadMore","android:loadMoreEnable"}, requireAll = false)
在上面的代码中,我们定义了3个属性,requireAll=false
代表我们在使用时,可以只使用其中一个属性,也可以三个属性都使用;如果requireAll=true
,代表我们定义的这三个属性都是必须要使用的,不然就会报错。
我们知道,默认情况下,如果某个View没有android:xxx=""
或者app:xxx=""
属性,那么我们在布局文件中是使用不了这些属性的,因为我们根本没有定义这些属性,所以使用不了。但在dataBinding的世界中,即便我们没有定义这些属性,我们依然可以通过android:xxx=""
或者app:xxx=""
的方式使用这些属性,只要对应的java方法中有setXxx()
方法即可。说起来可能很抽象,我们举个例子:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<import type="android.text.method.LinkMovementMethod"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="center"
android:text="BindingMethodsDemo"
app:movementMethod="@{LinkMovementMethod.getInstance()}"/>
</LinearLayout>
</layout>
在TextView中,只有setMovementMethod()
方法,而没有android:movementMethod=""
属性,但从上面的代码可以看到,我们通过app:movementMethod=""
的方式可以直接使用movementMethod
属性,之所以可以这么做,是因为dataBinding支持这么做,这也是dataBinding的特性之一,在dataBinding中,在编译时,dataBinding会根据属性名movementMethod
从TextView源码中寻找setMovementMethod()方法,并放入对应的参数,而在TextView中正好存在setMovementMethod()方法且参数类型也一样,这样就实现了即便android默认没有提供相关属性,但是只要有相关set方法,我们就可以使用对应属性,这个特性在上面说到@BindingMethod与@BindingMethods
时也提到过,是不是很爽很刺激呢?
如果你感觉还不够刺激,我们来个更刺激的:
- 如果一个view既没有android:xxx=""
或者app:xxx=""
属性,也没有setXxx()
方法,我们通过@BindingAdapter
同样可以实现自定义android:xxx=""
或者app:xxx=""
属性,然后使用。
如果你感觉还是不够刺激,我们还有终极大招:
- 如果我们要使用其他类中的方法,这些方法肯定是不属于这个View的,我们可以通过@BindingAdapter
自定义一个或一些属性,让我们可以在这个view中使用属性来操作其他类中的方法。一个最典型的使用场景就是RecyclerView与adapter,通过@BindingAdapter
,我们可以把adapter中的方法直接配置到RecyclerView的属性中去,就像这样:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loadMoreEnable="@{true}"
android:onItemClick="@{presenter.onItemClick}"
android:onLoadMore="@{presenter.onLoadMore}"
app:adapter="@{adapter}"
app:layoutManager="LinearLayoutManager"/>
理论总是枯燥的,下面通过2个示例来演示如何使用@BindAdapter
。
为ImageView设置url,placeHolder,errorDrawable
1. 使用@BindingAdapter
定义相关属性:
这里要注意的是:定义的属性的顺序与方法中参数的顺序必须保持一致,且方法中第一个参数必须是要操作的View。
public class ImageViewBindingAdapter {
* 为imageView设置url,placeHolder,error
@BindingAdapter(value = {"android:imageUrl", "android:placeHolder", "android:error"}, requireAll = false)
public static void loadImage(ImageView imageView, String url, Drawable placeHolder, Drawable error) {
ImageLoader.bind(imageView, url, placeHolder, error, true, DiskCacheStrategy.ALL, null);
- 使用自定义的属性
使用自定义属性的时候,对顺序没有要求,只要保证传入的值的类型与loadImage()
方法中对应参数的类型一致即可。
<variable
name="url"
type="String"/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:placeHolder="@{@drawable/placeholder}"
android:imageUrl="@{url}"
android:error="@{@drawable/error}"/>
为RecyclerView设置adapter,这里adapter使用BaseRecyclerViewAdapterHelper,一方面,主要是BaseRecyclerViewAdapterHelper可以额外设置一些方法,比如:setOnItemClickListener
,setOnLoadMoreListener
,setEnableLoadMore
等,在演示@BindingAdapter
用法时能更加说明问题;另一方面,也想凸显出简化代码的幅度,当BaseRecyclerViewAdapterHelper结合DataBinding之后,你会发现写adapter变得更加简单,就像下面这样:
public class OrderListAdapter extends BaseQuickAdapter<OrderDetailInfo, BaseViewHolder> {
public OrderListAdapter() {
super(R.layout.item_order_list_layout);
@Override
protected void convert(BaseViewHolder helper, OrderDetailInfo item) {
ItemOrderListLayoutBinding binding = DataBindingUtil.bind(helper.itemView);
binding.setOrderInfo(item);
binding.executePendingBindings();
- 使用
@BindingAdapter
定义相关属性:
我们针对setOnItemClickListener
,setOnLoadMoreListener
,setEnableLoadMore
这三个方法定义三个属性:
public class RecyclerViewBindingAdapter {
@BindingAdapter(value = {"android:onItemClick", "android:onLoadMore",
"android:loadMoreEnable"}, requireAll = false)
public static void setupAdapter(RecyclerView recyclerView, final ItemClickListener itemClickListener,
final LoadMoreListener loadMoreListener, final boolean loadMoreEnable) {
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter == null || !(adapter instanceof BaseQuickAdapter)) {
return;
BaseQuickAdapter quickAdapter = (BaseQuickAdapter) adapter;
quickAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
itemClickListener.onItemClick(adapter, view, position);
quickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
@Override
public void onLoadMoreRequested() {
loadMoreListener.onLoadMore();
}, recyclerView);
quickAdapter.setEnableLoadMore(loadMoreEnable);
quickAdapter.setLoadMoreView(new RVLoadMoreView());
quickAdapter.openLoadAnimation(BaseQuickAdapter.ALPHAIN);
public interface ItemClickListener {
void onItemClick(BaseQuickAdapter adapter, View view, int position);
public interface LoadMoreListener {
void onLoadMore();
- 使用自定义的属性
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loadMoreEnable="@{true}"
android:onItemClick="@{presenter.onItemClick}"
android:onLoadMore="@{presenter.onLoadMore}"
app:adapter="@{adapter}"
app:layoutManager="LinearLayoutManager"/>
通过上面的方式,我们就实现了通过在RecyclerView中配置属性达到为adapter设置点击监听,上拉加载监听,以及是否开启监听的目的。
其中的app:adapter="@{adapter}"
是因为RecyclerView有setAdapter方法,结合databinding的特性,故而可以这样写。而app:layoutManager="LinearLayoutManager"
属性是RecyclerView自己提供的一个属性,为了方便我们为RecyclerView设置layoutManager,其内部采用反射构造一个目标layoutManager,然后通过RecyclerView的public void setLayoutManager(LayoutManager layout)
再进行设置。
自定义的属性是如何被调用的
我们以ImageView设置url,placeHolder,errorDrawable为例进行说明。把代码粘过来,防止大家再拉回去看代码:
<variable
name="url"
type="String"/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:placeHolder="@{@drawable/placeholder}"
android:imageUrl="@{url}"
android:error="@{@drawable/error}"/>
在编译时,dataBinding扫描ImageView中使用dataBinding表达式的属性【扫描的依据是属性值是不是以@{
开头的】,然后找到了android:placeHolder="@{@drawable/placeholder}"
,android:imageUrl="@{url}"
,android:error="@{@drawable/error}"
这3个属性,然后判断这3个属性是不是ImageView的自有属性,如果是自有属性,也会去找对应的set方法生成代码;如果不是自有属性,会先找有没有对应且合适的set方法,有的话,就用set方法生成代码,没有的话,就会从所有定义的BindingAdapter中去找合适的方法,显然,dataBinding会找到这段代码:
public class ImageViewBindingAdapter {
@BindingAdapter(value = {"android:imageUrl", "android:placeHolder", "android:error"}, requireAll = false)
public static void loadImage(ImageView imageView, String url, Drawable placeHolder, Drawable error) {
ImageLoader.bind(imageView, url, placeHolder, error, true, DiskCacheStrategy.ALL, null);
找到之后,就会调用我们自定义的loadImage方法,然后把相关参数放进去,这里就要求我们定义的属性与方法中的参数的顺序必须一致,这样dataBinding在生成代码时就知道android:imageUrl
的属性值应该放到参数的第二个位置,android:placeHolder
属性值应该放到参数的第三个位置,android:error
属性值应该放到参数的第四个位置。
dataBinding生成的代码如下所示:
com.qiangxi.databindingdemo.databinding.adapter.ImageViewBindingAdapter.loadImage(this.mboundView3, url, getDrawableFromResource(mboundView3, R.drawable.placeholder), getDrawableFromResource(mboundView3, R.drawable.error));
7.@InverseBindingAdapter
- 作用于方法,方法须为公共静态方法。
- 方法的第一个参数必须为View类型,如TextView等
- 用于双向绑定
- 需要与@BindingAdapter配合使用
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface InverseBindingAdapter {
String attribute();
String event() default "";
- attribute:String类型,必填,表示当值发生变化时,要从哪个属性中检索这个变化的值,示例:
"android:text"
- event: String类型,非必填;如果填写,则使用填写的内容作为event的值;如果不填,在编译时会根据attribute的属性名再加上后缀“AttrChanged”生成一个新的属性作为event的值,举个例子:attribute属性的值为”android:text”,那么默认会在”android:text”后面追加”AttrChanged”字符串,生成”android:textAttrChanged”字符串作为event的值.
- event属性的作用: 当View的值发生改变时用来通知dataBinding值已经发生改变了。开发者一般需要使用
@BindingAdapter
创建对应属性来响应这种改变。
@InverseBindingAdapter的简单示例
@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
public static String getTextString(TextView view) {
return view.getText().toString();
关于什么叫双向绑定以及@InverseBindingAdapter更加深入的用法,在下一篇文章【DataBinding使用教程(四):BaseObservable与双向绑定】中会有详细的说明,这里就不赘述了。
8.@InverseBindingMethod与@InverseBindingMethods
- @InverseBindingMethods注解用于标记类
- @InverseBindingMethod注解需要与@InverseBindingMethods注解结合使用才能发挥其功效
- 用法极其简单
- 用于双向绑定
- @InverseBindingMethods需要与@BindingAdapter配合使用才能发挥功效
用法示例:
@InverseBindingMethods({
@InverseBindingMethod(type = SeekBar.class, attribute = "android:progress"),
public class SeekBarBindingAdapter {}
@InverseBindingMethod
@Target(ElementType.ANNOTATION_TYPE)
public @interface InverseBindingMethod {
Class type();
String attribute();
String event() default "";
String method() default "";
- type:Class类型,必填,如:SeekBar.class
- attribute:String类型,必填,如:android:progress
- event:String类型,非必填,属性值的生成规则以及作用和@InverseBindingAdapter中的event一样。
- method:String类型,非必填,对于什么时候填什么时候不填,这里举个例子说明:比如SeekBar,它有
android:progress
属性,也有getProgress
方法【你没看错,就是getProgress
,不是setProgress
】,所以对于SeekBar的android:progress
属性,不需要明确指定method,因为不指定method时,默认的生成规则就是前缀“get”加上属性名,组合起来就是getProgress
,而刚才也说了,getProgress
方法在seekBar中是存在的,所以不用指定method也可以,但是如果默认生成的方法getXxx
在SeekBar中不存在,而是其他方法比如getRealXxx
,那么我们就需要通过method属性,指明android:xxx
对应的get方法是getRealXxx
,这样dataBinding在生成代码时,就使用getRealXxx
生成代码了;从宏观上来看,@InverseBindingMethod
的method属性的生成规则与@BindingMethod
的method属性的生成规则其实是类似的。
关于@InverseBindingMethod与@InverseBindingMethods更加具体的用法在下一篇文章【DataBinding使用教程(四):BaseObservable与双向绑定】中会有说明,这里同样就不再赘述了。
9.@InverseMethod
@InverseMethod
注解是一个相对独立的注解,不需要其他注解的配合就可以发挥它强大的作用,它的作用就是为某个方法指定一个相反的方法。它有一个String类型的必填属性:value,用来存放与当前方法对应的相反方法。
正方法与反方法的要求:
- 正方法与反方法的参数数量必须相同
- 正方法的最终参数的类型与反方法的返回值必须相同
- 正方法的返回值类型必须与反方法的最终参数类型相同。
举个例子大家应该就明白了:
@InverseMethod("convertIntToString")
public static int convertStringToInt(String value) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return -1;
public static String convertIntToString(int value) {
return String.valueOf(value);
@InverseMethod注解在一些特殊的业务场景中,可以大大简化我们的代码,简直爽的不行,在下一篇文章【DataBinding使用教程(四):BaseObservable与双向绑定】中,我会以一个典型示例演示如何使用@InverseMethod注解以及它的强大之处。
所有的注解总算是讲完了,关于双向绑定的注解只是讲了基本的用法,具体更详细的用法及使用场景会在下一篇文章中讲解。
现在终于发现,想写一篇好文章真的太难了,要考虑好多好多东西,生怕自己哪里讲错了误人子弟,就算即便这样,可能这篇文章也还算不上好文章,并且个人能力有限,文章中难免会有讲的错误或者不恰当的地方,还请广大读者不吝指出。
看到有读者要打赏,我反手就是两张收款码
文章有点长,请做好心理准备有哪些注解@Bindable@BindingAdapter@BindingBuildInfo@BindingConversion@BindingMethod@BindingMethods@InverseBindingAdapter@InverseBindingMethod@InverseBindingMethods@InverseMetho...
前言前几篇已经DataBinding的基础用法已经介绍的差不多了,所以接下来感受一下DataBinding强大的高级功能。正文DataBinding可以通过注解,实现很多强大的功能,并且十分的方便,能够帮助我们省去大量的对xml进行操作的代码,例如加载图片,字符串处理等等操作。@BindingConversion 自定义类型转换<ImageView
android:layout_width
一、
DataBinding使用
本文着重讲解
DataBinding原理,使用的例子比较简单,若读者想要了解更多的
DataBinding的使用方法介绍,可以自寻相关资料,本文纯属个人理解,若有错误,还望指出(抱拳)
在app模块的build.gradle中加入如下配置
android {
dataBinding {
enabled = true
kotlin编写的RecyclerView,使用
DataBinding进行双向绑定
实现了 BaseObservable 的数据类,其实也没啥必要,就一个String
具体效果就是修改一侧RecyclerView数据,另一侧也会同步变化
Unresolved reference: BR ->这个错误可以见我的上篇博客
直接上代码
class Use
Data() : BaseObservable() {
var use
Data: String = ""
DataBinding的双向绑定,可以实现控件和数据的双向绑定,比如在登陆场景下,如果你使用了一个UserModel来保存数据,当你修改UserModel中的字段时,Edittext会自动更新。并且,当你修改Edittext的输入内容时,UserModel也能得到同步修改
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/andro
Google开源的数据绑定框架, 实现了MVVM架构, 增强了xml的功能, 大幅度精简了java代码量, 并且代码可读性更高, 对性能的影响基本为零.
DataBinding会自动在build目录下生成类. 因为被集成进AndroidStudio所以不需要你手动编译会实时编译, 并且支持大部分代码补全.
启用DataBinding
android{
dataBinding {...
@
BindingAdapter
使用
BindingAdapter
注解来创建一个自定义的xml属性。当这个属性以正确的类型设置到布局文件中时,
data binding 框架会触发被
注解的方法。
注解方法必须是静态的。
对于一些View本身就没有android:xxxx或者app:xxx属性,我们是使用不了,但是...