高效开发 MVVM 和 databinding 你须要使用的工具

喜欢小之的文章的能够关注公众号「WeaponZhi」持续关注动态
java


相信很多同窗已经开始使用 MVVM做为本身 Android 开发架构了,但实际上,我在使用过程当中查阅资料发现,网上有关 MVVM 的资料并非不少,这主要是由于 MVVM 仍是有必定使用门槛的,而且 MVVM 不必定会帮助你提升开发效率,可能你须要写的代码更多了,或者说为了你为了让代码保持 Databinding 的双向绑定特性,而须要考虑不少业务之外的设计逻辑。咱们使用一个架构或者设计模式,固然是为了更好的开发体验嘛,因此我将给你们介绍几个实用的第三方库和工具,来帮助你们解决这些问题。

MVVMLight

「MVVMLight」这个第三方库其实是对 Databinding 工具库的一些扩展,而且经过ReplyCommandResponseCommand来对全部的 View 的事件进行统一封装,这是我认为 MVVMLight 最大的用处android

MVVMLight的官方介绍博客
MVVMLight的源码地址git

咱们来看一下ReplyCommand怎么用。咱们用常见的下拉刷新控件PullToRefreshLayout来举例子。github

咱们知道若是你想自定义一个控件的事件,你须要使用@BindingAdapter注解,好比ImageView经过URL属性直接根据地址下载图片并显示能够这样写:设计模式

@BindingAdapter("bind:urlImage")  
public static void getInternetImage(ImageView iv, String userface) {  
    Picasso.with(iv.getContext()).load(userface).into(iv);  
}复制代码
<ImageView android:id="@+id/iv" android:layout_width="wrap_content" android:layout_height="wrap_content" app:urlImage="@{user.urlImage}"/>复制代码

这种状况每每是比较简单的,由于只是操做一个属性,但咱们要自定义某一个事件该怎么办呢,好比咱们要自定义onClick事件,那可能就得写接口了:网络

@BindingAdapter("setImageOnClick")
    public static void setImageOnClick(ImageView imageView, final ImageOnClickListener listener){
        if (listener != null) {
            imageView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.onClick(v);
                }
            });
        }
    }

interface ImageOnClickListener{
    void onClick(View v);
}复制代码

使用的时候呢,你得在 VM 中定义一个ImageOnClickListener的成员变量listener,在里面写具体的onClick实现方法,而后在 xml 中经过app:setImageOnClick="viewModel.listener"来绑定这个事件。数据结构

固然,你能够直接经过android:onClick来进行绑定,这里只是实例。架构

看起来好像也不是很麻烦,可是你可能每个这样的事件,就得定义一个特殊的接口,咱们能不能封装一下呢?app

这就是 MVVMLight 中 ReplyCommand 和 ResponseCommand 作的事了。经过这两个类封装了各类请求参数数量和返回值参数数量的回调方法,在使用的时候,只要在泛型里具体指名请求参数和返回值的类型便可,能够说很方便了。ide

实例,PullToRefreshLayout 是一个刷新列表控件,咱们经过使用ReplyCommand监听下拉刷新和上拉加载的监听器是这样写的:

@BindView(R.id.refresh_listview)
PullToRefreshLayout pullToRefreshLayout;

...

@BindingAdapter (value = {"onRefreshCommand", "onLoadCommand"}, requireAll = false)
public static void onRefreshLoadCommand( final PullToRefreshLayout pullToRefreshLayout, final ReplyCommand onRefreshCommand, final ReplyCommand onLoadCommand) {

    pullToRefreshLayout.setOnRefreshListener(new PullToRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh(PullToRefreshLayout pullToRefreshLayout) {
            if (onRefreshCommand != null) {
                onRefreshCommand.execute();
            }
        }

        @Override
        public void onLoadMore(PullToRefreshLayout pullToRefreshLayout) {
            if (onLoadCommand != null) {
                onLoadCommand.execute();
            }
        }
    });
}复制代码

咱们使用统一的ReplyCommand来处理控件的各类事件,这里使用的是无参无返回值的最简单的状况,咱们在 ViewModel 和 xml 中的写法是和以前的接口差很少的:

public final ReplyCommand onRefreshCommand = new ReplyCommand(() -> getPostData(true));

public final ReplyCommand onLoadCommand = new ReplyCommand(()->getPostData(true));复制代码
<com.weapon.joker.lib.view.pullrefreshload.PullToRefreshLayout android:id="@+id/pull_refresh_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:onRefreshCommand="@{viewModel.onRefreshCommand}" app:onLoadCommand="@{viewModel.onLoadCommand}"/>复制代码

这样,咱们全部事件的接口就统一了。ResponseCommand 和 ReplyCommand 的区别主要在,ResponseCommand 是用来定义那种有返回值的参数的,而 ReplyCommand 是没有返回值的,具体的使用方法,你们能够参考上面的连接,做者本身讲的最详细。

binding-collection-adapter

「binding-collection-adapter」对全部须要adapter的控件进行了封装,好比一些经常使用的:ListViewRecyclerViewViewPager等,经过使用这个库,咱们就不须要再写 adapter 了,经过 databinding 的方式,在 xml 绑定一些属性,并在 ViewModel 中对这些属性进行处理便可完成这些控件的处理,逻辑清晰,代码简单。

GitHub 地址

下面举一个 RecyclerView 的例子。咱们如今 xml 中定义一个 RecyclerView 控件。

<android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="@{LayoutManagers.linear()}" app:items="@{viewModel.items}" app:itemBinding="@{viewModel.itemBinding}"/>复制代码

咱们看到有三个特殊的属性:layoutManageritemsitemBinding,这里的layoutManager你们都比较熟悉了,参数是在开头的import导入的,传入相关的类名便可。

<data>
    <variable name="viewModel" type="com.weaponzhi.test.ViewModel"/>
    <import type="com.weaponzhi.test.LayoutManagers"/>
</data>复制代码

咱们先来看一下itemBinding是干什么用的,咱们知道有时候列表项是可能多布局的,那么这个itemBinding就是用来处理每种布局和对应 item 的 ViewModel 的绑定关系的。上述代码的 ViewModel 中,定义了该itemBinding

public final OnItemBindClass<Object> itemBinding =
    new OnItemBindClass<>
    .map(NoDataViewModel.class,BR.noData,R.layout.listitem_no_data)
    .map(ItemViewModel.class,BR.itemVM,R.layout.listitem_page);复制代码

map方法中有三个参数,第一个参数是这个布局的 ViewModel,第三个参数是这个布局的 xml 文件,第二个参数这个 xml 中引入的 ViewModel 的 BR 文件 id。这样咱们就绑定好了这个列表控件的多布局逻辑了。一个空数据时候的布局,一个正常返回数据时候的布局。

那么咱们的数据是如何刷新的呢,这就要用到上面的items这个属性了,在咱们这个例子里,它是这样定义的:

public final ObservableList<Object> viewModels = new ObservableArrayList<>();复制代码

当咱们网络请求返回的时候,咱们在数据回调里,经过对数据类型的处理,进行ItemViewModel的构造,最后只须要将构造好的对象一个个添加到这个ObservableList数据结构中去,界面的刷新工做都在对应的ItemViewModel里中进行处理,咱们刚刚设置的itemBinding在这时候就起做用了,当新增数据的时候,它会先判断这个更新数据的ItemViewModel的数据类型,NoDataViewModel.class类型的,那么就使用R.layout.listitem_no_dataItemViewModel.class类型的,就使用R.layout.listitem_page。固然,其余的数据更新和删除操做,也会由于双向绑定而同步刷新。

咱们彻底从 Adapter 的繁琐中解放出来了!

Databinding support

这是一个 Android Studio 插件,咱们写 xml 中的一些 Databind 代码好比<layout><data><variable><import>等标签的使用仍是比较多的,并且写起来也比较繁琐,这个插件就是能够帮助你解放双手,只须要在适当的地方按⌥+⏎(Windows 是 Alt+Enter)便可,从官网盗几张 Gif 图给你们感觉一下吧。

Wrap with <layout></layout>

Add <data> tag

Wrap with @{}

Wrap with @={}

Switch @{} and @={}

Add <import>

Add <variable>

MVVM 自动代码生成

MVVM 和 MVP 这种架构并不必定会让咱们代码量减小,每个界面可能都要以一种固定的模式建立不少类,那咱们为何不经过一种自动代码生成工具来经过简单的配置就完成这些类的建立呢,Java 彻底就能够实现这些功能。网上有不少用 Java 实现的自动生成代码的方式,但每一个人实现的 MVP 和 MVVM 架构方式都不一样,因此自动化代码也会不一样,我来展现下我这边使用的过程吧。

我使用的 MVVM 代码生成工具的主要思路是比较简单粗暴的,经过一个 xml 文件配置一些属性,好比起一个名字,设置一下文件输出的路径,而后在 Java 里用字符串拼接和文件流读取的方式来生成模板代码。


我如今维护的一个项目中,使用了 MVVMLight 和 binding-collection-adapter 你们能够参考下。
GitHub 连接


期待您关注个人公众号:WeaponZhi

相关文章
相关标签/搜索