MVVM Light Toolkit使用指南

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

 

 

概述

 

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

源码:
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!

 

转载于:https://www.cnblogs.com/nimorl/p/9139250.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/485924.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

段永朝:界面——之内、之外与之间

来源&#xff1a;苇草智酷01画一个圈儿&#xff0c;就把平面分成圈内&#xff0c;圈外。这个圈儿&#xff0c;就成了界面。垒一道墙&#xff0c;就把空间分成墙里&#xff0c;墙外。这个墙&#xff0c;就成了界面。看一眼手机上的钟表&#xff0c;那一刻闪现的数字&#xff0c;…

mysql面试考点_mysql面试知识点

1 引擎-MyISAM和InnoDB的区别a 是否支持行级锁 :MyISAM 只有表级锁(table-level locking)&#xff0c;而InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。b 是否支持事务和崩溃后的安全恢复&#xff1a;MyISAM 强调的是性能&#xff0c;每次查询具有原子性,其执行…

jquery 快速入门二

---恢复内容开始--- 操作标签 样式操作 样式类 addClass();//添加指定的CSS类名。 removeClass();//移除指定的类名. hasClass();//判断样式不存在 toggleClass();//切换css类名&#xff0c;如果有就移除&#xff0c;如果没有就添加 示例&#xff1a;开关灯和模态框 CSS css(&q…

中国工程院《全球工程前沿2020》报告在京发布

来源&#xff1a;科学网作者&#xff1a;李晨阳12月18日上午&#xff0c;中国工程院《全球工程前沿2020》报告在京发布。报告围绕机械与运载工程、信息与电子工程、化工冶金与材料工程、能源与矿业工程、土木水利与建筑工程、环境与轻纺工程、农业、医药卫生、工程管理9个领域&…

AlphaFold证明人工智能可以解决基本的科学问题

来源&#xff1a;IEEE电气电子工程师Gif: DeepMindTwo examples of protein targets in the free modelling category.任何人工智能的成功实施都依赖于以正确的方式提出正确的问题。这就是英国人工智能公司DeepMind&#xff08;Alphabet的子公司&#xff09;在利用其神经网络解…

Java中文件的创建

File filenew File&#xff08;“text.txt”&#xff09; 运行这行代码&#xff0c;我们会发现相应的文件夹下无法找到创建的text.txt 首先 File 类是对文件系统的映射 并不是硬盘上真实的文件 所以 new File("xxx.xxx") 只是在内存中创建File文件映射对象,而并不会…

计算的极限(零):逻辑与图灵机

来源&#xff1a; 数学职业家提出问题和解决问题的人2012&#xff0c;图灵诞辰100周年&#xff0c;献给这位伟大的开拓者。计算无处不在。走进一个机房&#xff0c;在服务器排成的一道道墙之间&#xff0c;听着风扇的鼓噪&#xff0c;似乎能嗅出0和1在CPU和内存之间不间断的流动…

java修改list中对象的值_Java中List集合的一点总结

为什么要用接口引用指向实现类的对象&#xff1f;这种写法其实java多态的表现形式多态的定义&#xff1a;指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。&#xff08;发送消息就是函数调用List list;是在栈区开辟一个空间…

Science公布年度十大科学突破!新冠疫苗居首位

来源 &#xff1a; 新华社编辑&#xff1a;宗华排版&#xff1a;郭刚美国《科学》杂志17日公布其评选的2020年十大科学突破&#xff0c;“以创纪录的速度开发和测试急需的新冠疫苗”当选头号突破。中国、美国、欧洲等地科研人员在2020年年初获得新冠病毒基因组序列后&#xff0…

openlayers实例_介绍OpenLayers

简介Web开发有一个专门的方向就是Web GIS&#xff0c;而Openlayers库就是Web GIS里的一个翘楚&#xff0c;想要开源的Web GIS的JavaScript库几乎就没有别的选择。OpenLayers的官网是OpenLayers - Welcome​openlayers.org目前最新的版本是5.3.x。它的github地址是openlayers/op…

macos Jetbrains IDEA用户自定义vm配置信息存储路径, IDEA点击无反应 无法打开问题解决

Jetbrains Clion, IDEA 用户在应用里面修改了自定义的VM配置后的存储路径为 ~/Library/Application Support/JetBrains/xxx2023.3/xxx.vmoptions xxx为你安装的APP名称, 如 Clion .IntelliJIdea 这里的自定义配置如果配置有误就会直接导致JetBrains软件无法打开, 即 点击打开…

【sping揭秘】19、关于spring中jdbctemplate中的DataSource怎么来呢

我们这是可以正好借助之前学的factorybean类&#xff0c;自己吧jdbctemplate加载到spring容器中&#xff0c;我们可以封装多个这种对象&#xff0c;那么可以实现针对不同的数据库的jdbctemplate 首先我们肯定要引入对应的jar&#xff0c;来构建数据源对象 <dependency>&l…

2020年度“中国神经科学重大进展”获奖名单【附成果介绍】

来源&#xff1a;brainnews编辑&#xff1a;Simon为了推动神经科学领域的创新发展&#xff0c;充分展示和宣传神经科学领域的重大科研成果&#xff0c;经中国神经科学学会七届二次常务理事会一致通过&#xff0c;开展2020年度“中国神经科学重大进展”推荐工作。按照相关程序&a…

python选择某一行_Python常用语法有哪些 如何快速入门Python开发

Python常用语法有哪些&#xff1f;如何快速入门Python开发&#xff1f;Python编程语言是最容易学习、并且功能强大的语言&#xff0c;吸引了很多人学习入行。很多人想知道如何快速入门Python开发&#xff0c;接下来就给大家分享Python学习入门中常用的语法知识。1、变量。常量&…

人类使用AI征服太空技术盘点,《星球大战》中的机器人会成为现实吗?

来源&#xff1a;学术头条作者&#xff1a;Thomas Lawrence一说起太空 AI&#xff0c;你脑海中浮现的是这样的角色吗&#xff1f;图 | 电影《星球大战》的AI机器人&#xff1a;C-3PO和R2-D2&#xff08;来源&#xff1a;starwars.fandom.com)实际上&#xff0c;早在现代计算机问…

springboot jar中没有主清单属性_Spring Boot 常见错误及解决方法

导读Spring Boot 作为 Java 生态中最流行的开发框架&#xff0c;意味着被数以万计的开发者所使用。下面根据我们自身遇到的问题&#xff0c;加上用户提供的一些反馈&#xff0c;来大致梳理下 Spring Boot 的常见错误及解决方法。找不到配置&#xff1f;配置不对&#xff1f;配置…

Nature:2020年10大科学发现

文章来源&#xff1a;新浪探索在2020年即将结束之际&#xff0c;《自然》&#xff08;Nature&#xff09;杂志盘点了今年发表的科学新闻和研究观点&#xff0c;从中选出了十项最为重大的科学发现&#xff0c;既包括新冠病毒研究、压力如何导致白发、HIV治疗等医学方面的研究&am…

php mysql上传多张图片_PHP实现一次性多张图片上传功能

原标题&#xff1a;PHP实现一次性多张图片上传功能最近遇到一个需求&#xff0c;就是多张图片上传&#xff0c;按住Ctrl键能选择多张图片一次性上传&#xff0c;上传成功后数据库保存图片的路径及图片原来的文件名。该功能一般在比较成熟的内容编辑器插件中有&#xff0c;今天特…

【2018.6.7】阶段总结

颓废了好久-.- 不记得从什么时候开始的了吧- 1. 好像是去年初赛翻车&#xff1f;&#xff09;&#xff09;归于以外好像也是我不对&#xff0c;&#xff0c;&#xff0c;总之很多&#xff0c;&#xff0c;不对的地方吧&#xff0c;&#xff0c;&#xff0c;我&#xff0c;&am…

车联网支持实现无人驾驶的思考

来源&#xff1a;机器学习研究组订阅号/ 导读 /无人驾驶是车辆作为运载工具智能化、网联化发展的核心应用功能&#xff0c;也是车联网、智慧交通产业发展的核心应用服务。网联无人驾驶则是在车辆智能化基础上&#xff0c;通过车联网实现车与车、车与路等的互联和信息交互&#…