Android Jetpack系列——DataBinding 最佳实践

看了谷歌官方文章确实写的太简略了,甚至看完以后有不少地方还不知道怎么回事儿或者怎么用,那么接下来我将经过几篇文章全面介绍一下 DataBinding 以及 DataBinding 的使用。
GitHub传送门 欢迎Star 下载
若有任何问题 关注 “朝阳杨少爷” 公众号给我留言,我会及时回复。

写在前面

咱们经过两篇文章,分别介绍了 DataBinding(Android Jetpack系列——细说DataBinding
) ,以及 DataBinding 的简单使用(DataBinding 的简单使用
) ,这篇文章,咱们来介绍一下 DataBinding 最佳实践——Binding adaptersjava

我之因此说 Binding adapters 是 DataBinding 的最佳实践,是由于用过了才知道是真的好用!android

下面咱们就经过这篇文章全面的介绍一下 Binding adapters。数组

请耐心看完这篇文章,就知道真的好用!bash

DataBinding里的注解方法讲解

在正式介绍 Binding adapters 以前,咱们先了解一下 DataBinding 里的注解方法。app

@Bindable

用于数据更新自动刷新视图。框架

@BindingAdapter

这个注解用于支持自定义属性,或者是修改原有属性。注解值能够是已有的 xml 属性,例如 android:src、android:text等,也能够自定义属性而后在 xml 中使用。ide

列如官方示列当中,就介绍了个 「setPadding」 的例子。布局

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
    view.setPadding(padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom())
}
复制代码

接受多个属性的适配器。ui

@BindingAdapter(value = { "imageUrl", "error" }, requireAll = false)
fun loadImage(view: ImageView, url: String, error: Drawable) {
    Picasso.get().load(url).error(error).into(view)
}

复制代码

从上面,咱们能够注意到几个关键的地方:url

  1. 修饰的方法,必须是 public static
  2. 方法参数第一个要求必须是 View
  3. 方法名能够随意
  4. 最后一个 booblean 类型是可选参数。能够要求是否全部参数都须要填写,默认是true。
  5. 若是这里requireAll为false, 你没有填写的属性值将为null. 因此须要作非空判断。

这里须要特殊说明的是:

当发生冲突时,定义的绑定适配器将覆盖Android框架提供的默认适配器。

@BindingMethods

DataBinding默承认以在布局中使用setter方法做为自定义属性,

可是若是不是setter格式的方法就要使用BindingMethod注解了. 经过建立一个自定义属性来关联一个类中已有的方法。

该注解属于一个容器. 内部参数是一个@BindingMethod数组, 只能用于修饰类(任意类均可以, 类能够为空).

下面咱们看一看官方示例:

@BindingMethods(value = [
    BindingMethod(
        type = android.widget.ImageView::class,
        attribute = "android:tint",
        method = "setImageTintList")])
复制代码

这里须要注意的是,这个注解必须有三个属性。

  1. type:字节码
  2. attribute:属性
  3. method:方法

会在指定的字节码(type)中寻找方法(method), 而后经过你建立的布局属性(Attribute)来回调方法。

若是属性名和@BindingAdapter冲突会报错

该注解只是单纯地关联已有的方法, 并不能新增方法. 因此全都是注解的空类.

@BindingConversion

属性值自动进行类型转换

列如,咱们用的 android:background 属性是 Drawable 的,可是须要指定一个颜色值,而这个值是整数的。

那么我就须要用到了 @BindingConversion 注解。

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
复制代码

这里咱们就能够用带有bindingConversion注释的静态方法进行转换,以下所示:

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
复制代码

可是,绑定表达式中提供的值类型必须一致。不能在同一表达式中使用不一样的类型,列如以下的错误示范:

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
复制代码

经过,以上咱们能够注意到:

  1. 只能修饰 public static 方法。
  2. 任意位置任意方法名都不被限制。
  3. DataBinding自动匹配被该注解修饰的方法和匹配参数类型。
  4. 返回值类型必须喝属性setter方法匹配,且参数只能有一个。
  5. 要求属性值必须是@{}DataBinding表达式。

Binding adapters的使用实践

经过上面的介绍,咱们了解到了这几个注释方法,接下来,咱们就要开始使用这些方法。
下面就开始实践使用:

@Bindable

这个注解的理解仍是十分简单的。

使用 @Bindable 来标记的 get 方法,在编译时,会在BR类当中生成对应的字段,而后与 notifyPropertyChanged() 方法配合使用,当该字段中的数据被修改时,dataBinding 会自动刷新对应view的数据,而不用咱们在拿到新数据后从新把数据在setText()一遍,就凭这一点,dataBinding就能够简化大量的代码。

以此来实现双向绑定,关于双向绑定的内容,我会经过下一篇文章来详细讲述,如今先简单介绍一下使用。

实体类也能够不用继承BaseObservable

,而是实现Observable接口,可是须要自行处理一些接口方法逻辑,BaseObservable是实现Observable接口的类,内部已经作好了相关逻辑处理,因此选择继承BaseObservable相对简单一些。

接下来咱们看一下如何在代码里实现:

class StudentInfo : BaseObservable() {
    @get:Bindable
    var name: String? = null
    @get:Bindable
    var age: Int = 0
    @get:Bindable
    var sex: String? = null
    @get:Bindable
    var score: Int = 0
}
复制代码

这样,咱们的实体类就完成了。具体的使用方法和效果,咱们在以后讲解双向绑定的时候会着重介绍。

@BindingAdapter

这里咱们必须着重介绍一下 BindingAdapter 这个注解。这个多是咱们在以后的使用当中,最经常使用的一个注解。
这个注解厉害了!
除了从新定义已经有的方法,还能够定义新的属性!

列如,咱们有个View既没有android:xxx=""或者app:xxx=""属性,也没有setXxx()方法,咱们经过@BindingAdapter一样能够实现自定义android:xxx=""或者app:xxx=""属性,而后使用!

除了定义属性职位,咱们还能够定义一些不属于这个View的属性!
咱们能够经过 @BindingAdapter 自定义一个或者一些属性,让咱们能够在这个View当中,使用相应的属性!

例如咱们定一个ImageView经过 @BindingAdapter 来定义一些属性。

@BindingAdapter(value = {"android:imageUrl", "android:placeHolder", "android:error"}, requireAll = false)
    public static void loadImage(ImageView view, String url, Drawable error, Drawable placeHolder) {
        Glide.with(view.getContext()).load(url).into(view);
    }
复制代码

定义好以后,咱们就能够开始使用了!

<ImageView
                android:id="@+id/iv_binding_adapter"
                android:layout_width="wrap_content" android:layout_height="wrap_content"
                android:placeHolder="@{@drawable/ic_launcher"
                android:imageUrl="@{url}"
                android:error="@{@drawable/ic_launcher}"
        />
复制代码

值得注意的是,这里的 @drawable/ic_launcher,用@{}括住资源使其成为有效的绑定表达式。

可是,我有一点疑惑的是,如今咱们图片的资源都放在mipmap系列文件夹下了,可是这里设置只能在drawable文件夹下面找到对应的资源?若是有什么想法和办法欢迎给我留言,咱们交流一下!

还有一点注意的是,咱们设置glide的时候,别忘了在AndroidManifest文件当中把权限设置上!

<uses-permission android:name="android.permission.INTERNET" />
复制代码

接下来,咱们就能够看到咱们要实现的效果了

是否是很厉害!这样,咱们能够节约多少代码!

以上只是一个简单的使用,还有更厉害的!
那就是配合RecyclerView设置adapter。RecyclerView能够说是咱们最经常使用的一个控件,若是吧adapter和DataBinding结合以后,你会发现写Adapter会变得十分的简单!
更多的属性,均可以在xml当中完成。

咱们能够为RecyclerView的Adapter当中,设置咱们经常使用的一些属性,好比 setOnItemClickListener 、 setOnLoadMoreListener 、
setEnableLoadMore 、setOnRefreshListener
等等这些咱们经常使用的一些方法。
列如,咱们能够定义一个BindAdapter

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)再进行设置。

最后

相信,经过上面的内容。已经能体会到了DataBinding的便捷之处。接下来,咱们在讲讲双向绑定。若有任何问题,欢迎给我留言,咱们一块儿讨论。

GitHub传送门 欢迎Star 下载
若有任何问题 关注 “朝阳杨少爷” 公众号给我留言,我会及时回复。

扫一扫,即刻加入到专属限免的星球当中,这里有不少有意思的人,好玩儿的事儿等你来耍!

相关文章
相关标签/搜索