原文:MVVM Light Toolkit使用指南

原文地址:  https://blog.csdn.net/ldld1717/article/details/77040077

概述

MVVM Light Toolkit是一个Android MVVM 轻量级工具库,主要目的是更快捷方便的构建Android MVVM应用程序,工具库添加了一些Data Binding 不支持的属性,还有添加对控件事件的封装,同时提个一个全局消息通道方便ViewModel 之间的通信,Toolkit主要包括两部分Binding和Messenger,接下来,我们分别说明下这两个模块的作用和使用方法。

源码:
Github:https://github.com/Kelin-Hong/MVVMLight

compile 'com.kelin.mvvmlight:library:1.0.0'

Binding


MVVM Binding Schematic Diagram

由上图我们可以看到,在View和ViewModel的绑定中,包含两种绑定,一种是数据的绑定(比如:TextView:text),另外一种命令绑定,命令绑定我们可以理解为事件绑定,(比如:Button:click),但是目前Databinding 并不完全支持命令的绑定, 而且对Data的绑定的支持也不完善(比如说不支持AdapterView对DataSource的绑定),那么在MVVM LightBinding我们添加了部分Data绑定支持和Command绑定的支持。

  • Data 绑定
    我们添加了(ImageView:uri、placeholderImageRes),(ListView:views、itemView) 等部分控件的一些属性,这使得我们用起来就方便很多,例如我们只要在xml布局<ImageView>标签中设置uri,那么这个图片就能自动去加载这个uri的图片,如下:

    <ImageView      android:layout_width="match_parent"    android:layout_height="match_parent"    android:layout_alignParentRight="true"       bind:uri="@{viewModel.imageUrl}"  bind:placeholderImageRes="@{R.drawable.ic_launcher}"/>

    当然ImageView还支持其他属性

    placeholderImageRes :图片还没有下载完成时的替换图片
    request_width : 请求图片的宽度,会自动帮你裁剪
    request_height:请求图片的高度,会自动帮你裁剪

接下来看下示例:


image.gif

对于ListView、RecyclerView 和 ViewPager 等AdapterView使用起来就更加简单了,在布局文件添加bind:itemView(子布局模板),bind:items(数据源)还有bind:layoutManager就能ok了,完全不需要写Adapter相关的东西。

  <android.support.v7.widget.RecyclerView        android:layout_width="match_parent"         android:layout_height="match_parent"         bind:itemView="@{viewModel.itemView}"         bind:items="@{viewModel.itemViewModel}"         bind:layoutManager="@{LayoutManagers.linear()}"

然后Java 代码中在ViewModel定义itemView和itemViewModel就可以了列表就能展示出来了

public final ObservableList<ViewModel>  itemViewModel = new  ObservableArrayList<>();  public final ItemView itemView = ItemView.of(BR.viewModel, R.layout.layoutitem_list_view);

动画示例:


listview_databinding.gif

关于AdapterView 数据源绑定问题主要还是Google的DataBinding框架还不够完善,相信不久后肯定有更好的方案出来,让我们更加方便把数据源和布局模板绑定到ListView上,当然现在也有一些开源库帮我们把这个部分做好了,我们也不重复造轮子,就直接使用了关于AdapterView的一些数据绑定扩展,https://github.com/evant/binding-collection-adapter 给了比较详细的描述,基本能满足大部分AdapterView的需求。

接下来列举目前MVVM Light 工具库支持的一些Data Binding的属性:

  • ImageView

    <attr name="uri" /> <!--width for ResizeOptions (use Fresco to load bitmap). --> <attr name="request_width" format="integer" /> <!--height for ResizeOptions (use Fresco to load bitmap). --> <attr name="request_height" format="integer" /> <attr name="placeholderImageRes" format="reference|color" />
  • ListViewViewPagerRecyclerView

     <!-- require ItemView  or  ItemViewSelector   -->  <attr name="itemView" />  <!-- require List<ViewModel> bind to ItemView to presentation.-->   <attr name="items" />  <!-- require a adapter which type of BindingRecyclerViewAdapter<T> to AdapterView-->  <attr name="adapter" />  <attr name="dropDownItemView" format="reference" />  <attr name="itemIds" format="reference" />  <attr name="itemIsEnabled" format="reference" />  <!-- require PageTitles<T>-->  <attr name="pageTitles" format="reference" />
  • ViewGroup

     <!-- require ItemView  or ItemViewSelector -->  <attr name="itemView" />  <!-- require List<ViewModel> bind to ItemView to presentation.-->  <attr name="viewModels" format="reference" />
  • EditText

     <!-- require boolean value to decide whether requestFocus for view. -->  <attr name="requestFocus"  format="boolean" />
  • SimpleDraweeView

     <!-- require String to load Image"-->  <attr name="uri" />
  • WebView

     <!-- require String render to html show in webview-->  <attr name="render" format="string" />

目前整理的一些常用属性和控件可能不足,我们也不太可能能把所有可用的控件和控件关联的属性全部做一些封装,希望开发者自己可以在需要的时候去继承相应的类扩展更多的属性。

  • Command 绑定
    Command 翻译为命令,就是控件发号施令,然后有人去回复处理这个命令,比如Button 发出一个Click Command,那么应该有个处理者来处理这个命令,我们把这个处理者叫做ReplyCommand,我们把ReplyCommand绑定到相应的控件上,如果控件发出Event的时候,就会找到这个ReplyCommand让它来处理这个事件,说得简单一些就是我们对控件一些事件做了简单的封装,使得处理这些事件我们只有关注我们相应事件传递给我们想要的数据,其他UI相关的逻辑并不需要我们去关心。接下来我们简要说明具体使用方法:

       <android.support.v7.widget.RecyclerView           android:id="@+id/recyclerView"           android:layout_width="match_parent"           android:layout_height="match_parent"           bind:onLoadMoreCommand="@{viewModel.loadMoreCommand}"/>

    bind:onLoadMoreCommand: RecyclerView 在滑到最底部的时候自动触发这个事件,这个事件的处理者是一个ReplyCommand<Integer>类型的变量

    接下来我们在ViewModel 里面的定义一个ReplyCommand<Integer> 变量

     public final ReplyCommand<Integer> loadMoreCommand = new ReplyCommand<>(   (count) -> {       /*count 代表当前RecyclerView 有多少个Item,通过这个值我们可以         得到当前应该去加载第几页的数据*/        int page=count / LIMIT;        loadData(page)   });

    对于处理RecyclerView 下拉加载更多,我们之前的做法可能需要去写各种代码来判断是否滑动底部,在OnScrollListener 里面做一些计算然后才知道滑到底部了,但是如果用封装好的Command事件,你的ViewModel里面的代码将非常简洁,只要简要的声明一个 ReplyCommand<Integer>,它就能自动传一个当前List中Item的总数给你,你要做的只是专注你的业务处理(比如 加载数据),ViewModel 里面只处理数据,不处理控件,没有任何控件的引用,所以UI的状态和变化都由数据来绑定控制的,这样数据就成了主角,我们在ViewModel 里面只要和数据打交道就可以了。


动画示例:


listview load more

同时对于我最常见的点击事件,我们也做对了click事件的封装,封装成一个clickCommand,接受的参数是一个无参的ReplyCommand:


动画示例:


clickCommand.gif

关于下拉刷新控件SwipeRefreshLayout的下拉刷新事件也简单封装成另一个ReplyCommand

动画示例:


refresh.gif

接下来列举目前MVVM Light 工具库支持的一些Command Binding的属性:

  • View

    <!-- require ReplyCommand to deal with view click event. --> <attr name="clickCommand" format="reference" /> <!-- require ReplyCommand<Boolean> to deal with view focus change event.  ReplyCommand would has params which means if view hasFocus.--> <attr name="onFocusChangeCommand" format="reference" /> <!-- require ReplyCommand<MotionEvent> --> <attr name="onTouchCommand" />
  • ListViewRecyclerView

    <!-- require ReplyCommand<Integer> --> <attr name="onScrollStateChangedCommand" /> <!-- require ReplyCommand<ListViewScrollDataWrapper> -->   <attr name="onScrollChangeCommand" /> <!-- require ReplyCommand<Integer> count of list items--> <attr name="onLoadMoreCommand" format="reference" />
  • ViewPager

     <!--require ReplyCommand<ViewPagerDataWrapper> --> <attr name="onPageScrolledCommand" format="reference" />   <!--require ReplyCommand<Integer> --> <attr name="onPageSelectedCommand" format="reference" /> <!--require ReplyCommand<Integer> --> <attr name="onPageScrollStateChangedCommand" format="reference" />
  • EditText

    <!--require ReplyCommand<TextChangeDataWrapper> --> <attr name="beforeTextChangedCommand" format="reference" /> <!--require ReplyCommand<TextChangeDataWrapper> -->         <attr name="onTextChangedCommand" format="reference" />  <!--require ReplyCommand<String> -->  <attr name="afterTextChangedCommand" format="reference" />
  • ImageView

     <!--  require ReplyCommand<Bitmap> -->  <attr name="onSuccessCommand" format="reference" />  <!--require ReplyCommand<CloseableReference<CloseableImage>> -->  <attr name="onFailureCommand" format="reference" />
  • ScrollViewNestedScrollView

    <!-- require ReplyCommand<ScrollDataWrapper> --> <attr name="onScrollChangeCommand" /> <!-- require ReplyCommand<NestScrollDataWrapper> --> <attr name="onScrollChangeCommand" />
  • SwipeRefreshLayout

    <!-- require RelayCommand<> --> <attr name="onRefreshCommand" format="reference" />

    以上的Command有继承效应,即ImageView、TextView是View的子类,所以它们都拥有View 扩展的Data Binding和Command Binding 如:clickCommand 等。

注:

  • MVVMLight 目前只支持上面提及的部分的控件Data和Command绑定,还有大部分的控件一些属性和事件并没有去封装,我们希望您根据自己的需求去补充,包括一些你用的自定义控件和第三方控件,都可以简单封装成一个Command,这样使得我们的ViewModel 更加简洁同时是我们更能专心处理数据和业务,不关心控件和UI。
  • 上面涉及的属性和Command,我们在attrs文件都已经声明,您在xml文件使用中将可以自动提示相应的名称,比如你在TextView中打bind:的时候和TextView相关的属性都会提示(如:requestFocus、clickCommand、等相关的属性),但是属性后面需要哪种ReplyCommand<T>,即T应该是什么类型,就需要查一下相应的文档、或者查一下源码。
  • 由于没办法做到在xml输入属性(如bind:clickCommand)自动提示应该输入什么值接入什么参数,所以这时候最简单的方式就是去查源码,我们把每个控件都单独放到一个包里面,方便查找源码。如果你没找到源码的位置,你可以shift+command+o 然后输入ViewBindingAdapter(也就是打开文件ViewBindingAdapter.class 或者 ViewBindingAdapter.java )然后选择相应的控件,去查看该控件的支持哪些属性和Command同时可以查看需要的ReplyCommand的类型。
  • 虽然是ViewBindingAdapter 里面的方法是static,但是是支持重写的,保持参数和@BindingAdapter(value =*) 的值不变,DataBinding 的找方法的时候就优先采用你写的方法,而不是库里的方法,也就是如果你对ViewBindingAdapter的里面的static方法的实现不满意的是可以重写的。

Messenger

引入messenger最主要的目的就实现ViewModel和ViewModel的通信,也可以用做View和ViewModel的通信,但是并不推荐这样做。ViewModel主要是用来处理业务和数据的,每个ViewModel都有相应的业务职责,但是在业务复杂的情况下,可能存在交叉业务,这时候就需要ViewModel和ViewModel交换数据和通信,这时候一个全局的消息通道就很重要的。接下来我们简单看一下messenger的使用:

  • 全局广播消息,没有参数
       /* 全局发送一个消息,但是不传任何参数给接收者   TOKEN: 相当于broadcast的Action,谁注册了这个令牌,相当于准备接收这个消息*/   Messenger.Default().sendNoMsg(TOKEN);    /* context: 一般是activity,代表接收者,用于在取消注册的时候使用unRegister(context)可快速取消这个context里面的所有注册       TOKEN: 相当于broadcast的Action,谁注册了这个令牌,相当于准备接收这个消息       (data)->{ }: 处理消息的Action */    Messenger.Default().register(context, TOKEN, () -> { });
  • 全局广播消息,带有参数
     /*发送消息,传递参数data(任何类型都可),TOKEN 是一个令牌(String类型)相当于   broadcast的Action,谁注册了这个令牌,相当于准备接收这个消息*/  Messenger.getDefault().send(data, TOKEN)   /*  context: 同上    * TOKEN: 相当于broadcast的Action,谁注册了这个令牌,相当于准备接收这个消息    * Data.class: 传递过来参数的类型(如 String.class,Model.class)    * (data)->{ }: 处理消息的function,参数是传递过来的 */  Messenger.getDefault().register(context, TOKEN, Data.class, (data) -> { });

示例:



  • 发送到指定的接收器(不常用)
     /*target 代表接收对象,一般是Activity(ViewModel 持有Activity的引    用),直接将消息发到指定的目标,而不是广播*/ Messenger.getDefault().sendToTarget(T message, R target) Messenger.getDefault().sendNoMsgToTargetWithToken(Object token,R target) Messenger.getDefault().sendNoMsgToTarget(Object target)
  • 取消注册

    /*一般在Activity的OnDestroy()方法中调用就可以, 因为我们一般注册的时候, 第一个参数   recipient就是传的当前的context,所以只要取消注册,这个Activity就再也收不到任何的消息了*/  Messenger.getDefault().unregister(Object recipient)"

    Messenger在全局虽然传递数据和通知非常方便,但是建议不要滥用,消息通知太多也意味着耦合性太高,代码框架设计不够友好,同时影响代码的可读性,ViewModel和View最好是用绑定的方式去处理UI和事件,ViewModel 和ViewModel 一些数据的通信交互才用Messenger来传递会好一些。

    注:

    1、Messenger 一定要取消注册,不然会造成严重的内存泄露
    2、Messenger 的TOKEN在命名的时候要注意,最好和相关的ViewModel结合起来,避免在同一个App出现两个相同的Token,这样会把消息发到其他你不知道的地方,造成潜在的BUG!

MVVM Light Toolkit使用指南的更多相关文章

  1. Mvvm Light Toolkit for WPF/Silverlight系列之搭建mvvmlight开发框架

    Mvvm Light Toolkit for WPF/Silverlight系列之搭建mvvmlight开发框架   本章节,我将通过示例介绍如何搭建mvvmlight开发环境.示例中的我会针对wpf ...

  2. MvvmLight学习篇—— Mvvm Light Toolkit for wpf/silverlight系列(导航)

    系列一:看的迷迷糊糊的 一.Mvvm Light Toolkit for wpf/silverlight系列之准备工作 二.Mvvm Light Toolkit for wpf/silverlight ...

  3. How to install MVVM Light Toolkit via NuGet

    Here is how you can install MVVM Light Toolkit  via NuGet in an easy way using only Visual Studio. S ...

  4. Mvvm Light Toolkit 入门

    原文:Mvvm Light Toolkit 入门 前言 之前学习UWP的时候就一直看到有关MVVM的资料但是一直没有系统的去学,最近正好有时间,特地来攻破这个点,顺便学习一下VS与GitHub的链接和 ...

  5. MVVM Light须要注意的10个问题

    MVVM Light须要注意的10个问题 从使用XAML技术基础開始(实际上并非非常久曾经).我便关注MVVM(Model – View – ViewModel)模式.偶然接触到MVVM Light不 ...

  6. MVVM Light 笔记

    4.关于子视图, MVVMLight Using Two Views:http://www.codeproject.com/Articles/323187/MVVMLight-Using-Two-Vi ...

  7. 【MVVM Light】Messager的使用

    一.前言       在MVVM编程的模式中,有时候我们会遇到一个很尴尬的情况: 若干个xaml.cs都复用一个ViewModel,当ViewModel想传递一个特定的消息给某一个xaml.cs的时候 ...

  8. 【MVVM Light】新手初识MVVM,你一看就会

    一.前言 作为一个初入软件业的新手,各种设计模式与框架对我是眼花缭乱的.所以当我接触到这些新知识的时候就希望自己能总结几个步骤,以便更好更方便的在日常工作中进行使用. MVVM顾名思义就是Model- ...

  9. MVVM Light中的Message

    比喻:像漂流瓶一样发送一个Message,任何人有兴趣就可以拾起来. MVVM Light中的Message的使用分为三个步骤: 1.创建一个类,包含要传递的Message. 2.在ViewModel ...

随机推荐

  1. [Android] ACTION_GET_CONTENT与ACTION_PICK的区别

    参考:AndroidSDK/docs/reference/android/content/Intent 看到相关代码,对这两个参数的区别有点搞不清. 原文地址请保留http://www.cnblogs ...

  2. cuda中用cublas库做矩阵乘法

    这里矩阵C=A*B,原始文档给的公式是C=alpha*A*B+beta*C,所以这里alpha=1,beta=0. 主要使用cublasSgemm这个函数,这个函数的第二个参数有三种类型,这里CUBL ...

  3. 【转】qt-vs-addin:Qt4和Qt5之VS插件如何共存与使用

    原则上,两者是不可以同时存在的,但是如果都安装了,该如何分别使用他们呢? Qt4 Visual Studio Add-in:官网可以下载安装程序,qt-vs-addin-1.1.11-opensour ...

  4. 安装并使用pt-ioprofile

    pt-ioprofile,是一个percona的性能分析工具,可以查看进程输出.输入,打印一些表文件及活动IO.pt-ioprofile是一个只读工具,所以对数据没风险.由于网上对pt-ioprofi ...

  5. 一次失败的生产系统中AlwaysOn AG切换经历

    14:25分左右,某数据库主副本服务器崩溃报错,在数据库无法接收SQL语句进行调整的情况下重启了主副本服务器. 由于服务器重启时间会比较长,为了保证主副本服务器重启期间数据库能正常进行写入,强制将主库 ...

  6. META-INF下文件读取

    protected String getProperty(String name) {        String file = "/META-INF/jndi.properties&quo ...

  7. oracle 使用绑定变量极大的提升性能

    初始化操作 SQL> alter system flush shared_pool; SQL> set timing on; 1. 未使用绑定变量的时候代码如下 declare type ...

  8. 安装SQL sever2008时显示重新启动计算机规则失败,应该怎么解决?

    1.删除注册表:在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager中找到 PendingFileRenameOpe ...

  9. 单点登录SSO的实现原理 (转)

    单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里使用得 ...

  10. UIButton vs UIEventListener 事件处理

    NGUI的事件 在使用NGUI的事件处理时,发现UIButton和UIEventListener之间的共同点越来越多. 当然处理事件,也有一些其它的函数,比如:UIEventTrigger,Butto ...