MVVM Light Toolkit使用指南

作者: Kelin | 来源:发表于2016-06-15 10:53 被阅读26796次

    概述

    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" />

      <attr name="request_width" format="integer" />

      <attr name="request_height" format="integer" />
      <attr name="placeholderImageRes" format="reference|color" />

    • ListViewViewPagerRecyclerView

      <attr name="itemView" />

      <attr name="items" />

      <attr name="adapter" />
      <attr name="dropDownItemView" format="reference" />
      <attr name="itemIds" format="reference" />
      <attr name="itemIsEnabled" format="reference" />

      <attr name="pageTitles" format="reference" />

    • ViewGroup

      <attr name="itemView" />

      <attr name="viewModels" format="reference" />

    • EditText

      <attr name="requestFocus" format="boolean" />

    • SimpleDraweeView

      <attr name="uri" />

    • 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

      <attr name="clickCommand" format="reference" />

      <attr name="onFocusChangeCommand" format="reference" />

      <attr name="onTouchCommand" />

    • ListViewRecyclerView

      <attr name="onScrollStateChangedCommand" />

      <attr name="onScrollChangeCommand" />

      <attr name="onLoadMoreCommand" format="reference" />

    • ViewPager

      <attr name="onPageScrolledCommand" format="reference" />

      <attr name="onPageSelectedCommand" format="reference" />

      <attr name="onPageScrollStateChangedCommand" format="reference" />

    • EditText

      <attr name="beforeTextChangedCommand" format="reference" />

      <attr name="onTextChangedCommand" format="reference" />

      <attr name="afterTextChangedCommand" format="reference" />

    • ImageView

      <attr name="onSuccessCommand" format="reference" />

      <attr name="onFailureCommand" format="reference" />

    • ScrollViewNestedScrollView

      <attr name="onScrollChangeCommand" />

      <attr name="onScrollChangeCommand" />

    • SwipeRefreshLayout

      <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!

    刚新建了博客答疑交流群,对博客内容有任何疑问或者我的几个开源库有任何使用的问题,还有喜欢捣鼓Android MVVM 同学,可以加入此群,大家一块探讨交流,共同学习😁

    博客答疑交流群

    相关文章

      网友评论

      • nanchen2251:原来是美团的大咖呀。
      • e357522907b2:想问一下,ItemViewSelect中的viewTypeCount()方法是recyclerview中有多少种vie就返回多少吗?
      • 贝贝beibei96:Error:FAILURE: Build failed with an exception.

        * What went wrong:
        Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
        > java.io.IOException: Could not delete path 'D:\AndroidProject\Webb\app\build\intermediates\transforms\mergeJavaRes\debug\0.jar'.

        * Try:
        Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

        * Get more help at https://help.gradle.org

        BUILD FAILED in 20s

        报这个错误啊!
      • vison123:你好,楼主,我的一直报这个错误---java.lang.IllegalStateException: Could not bind variable 'rvViewModel' in layout 'huainan.kidyn.cn.mvvmtest:layout/item_layou,提示recycleview的子布局没有绑定到它相应的viewModel,能问下是怎么回事吗
      • 1658db3ec3c5:..在xml 里写 <data> variable 然后 那个type填的 是自己建的 还是自动生成的。。
      • 菜头中:你好,看了下多篇文章质量都很高,想问下多种颜色层次分明图片都是自己画的嘛?用什么工具画的?谢谢
      • 故事细腻言不由己:为什么我的刷新有效果,上拉加载没反应呢
        Kelin:onLoadMoreCommand 需要接受一个参数的 ReplyCommand: public final ReplyCommand<Integer> loadMoreCommand = new ReplyCommand<>(
        (count) -> {
        });
        你再试试,它是RecyclerView 的Command
      • 我喂自己袋丶夏洛克:你好,我最近在学习使用你这个库,我有一点疑问,就是recyclerview绑定items, 请求数据,遍历,传统的方式自然是遍历后调用notifyDataSetChanged()来刷新.那如果用你的这个库的话,ObservableList.add(viewmodel),这时候recyclerview什么时候会进行界面刷新了?按照观察者模式来说,岂不是每add一个viewmodel就要刷新一次?
        Kelin:每add一个viewmodel 就是更新UI列表,是局部更新,相当于调用了notifyItemInserted(pos)的方法
        故事细腻言不由己:顺便问一句,使用onLoadMoreCommand能刷新到数据吗 我的上拉加载没反应啊
      • 草幽林:楼主 ,能否搞个群 关于databinding 或者这个mvvm的使用,探讨下 ,因为有些依赖库的版本在更新,我想更新但不知如何解决相关问题?
        linlin001991:新项目开始了,正打算用这个框架,怕坑太多哦,跪求问题解决群,大家一起维护哈。
        mikukon: @Kelin viewModel之间通过messeger交互如果是一次性的短暂消费事件感觉还行,但是如果是持续性的事件会不会过多的遍历list呢
        Kelin:最新 赶新项目,太忙了,博客都好久没更新了,如果建个群,我怕没有时间维护和帮忙解决一些问题呢:joy:
      • 草幽林:Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
        > java.lang.RuntimeException: Found data binding errors.
        ****/ data binding error ****msg:Cannot find the setter for attribute 'app:onItemSelectedListenerCommand' with parameter type com.kelin.mvvmlight.command.ReplyCommand<java.lang.Integer> on android.widget.Spinner.
        file:E:\Company\Project\zn_purchase_cloud\PCloud\app\src\main\res\layout\activity_todo_message.xml
        loc:37:49 - 37:87
        ****\ data binding error ****

        楼主有没有遇到过这个问题 我自己定义的绑定事件?
        谢谢
        Kelin:Cannot find the setter for attribute 'app:onItemSelectedListenerCommand' ,应该是你声明BindingAdapter 参数名字或者类型写错了
      • de548c4905e4:为什么使用了之后我用6.0测试会出现手机硬件不支持呢?我用魅族mx4测试没有问题,用魅蓝E测试就弹出Your hardware does not support this application
      • 草幽林:我在EditText的app:afterTextChangeCommand=“afterTextChangeCommand”绑定中,并没有执行我在viewmodel中定义的relayCommand方法。我想问下是有什么没有设置还是有什么问题导致。
        使用的网络依赖你类库:compile 'com.kelin.mvvmlight:library:1.0.0'
        viewmodel代码:
        public final ReplyCommand<String> afterTextChangedCommand = new ReplyCommand<>(() -> {
        Log.e("commond", "invoke");
        receiptAmount.set(String.valueOf(Double.valueOf(mAcceptanceCheckListInfoModel.get().getTransactionPrice()) * Double.valueOf(actualReceivedCount.get())));
        });

        xml代码:
        <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="@dimen/margin_16dp"
        android:layout_marginRight="@dimen/margin_16dp"
        android:clickable="false"
        android:inputType="number"
        android:text="@={viewModel.actualReceivedCount}"
        bind:afterTextChangedCommand="@{viewModel.afterTextChangedCommand}"
        bind:requestFocus="false"
        />
        如果您有时间,请解答我的困惑。谢谢!(我也发邮件到你google邮箱,没有发现你这里的博客,呵呵,如果这里回复,就不用回复邮件,抱歉)
        草幽林: @草幽林 好的,我在试试,感谢
        Kelin:这个是EditText 文本改变的时候回调,会把当前EditText的文本当做参数带回来,你改成
        public final ReplyCommand afterTextChangedCommand =new ReplyCommand<>((str) -> {
        //str 当前EditText改变后的文本
        }
        :smile:
        草幽林:在此调试发现 可以进入到如下代码:
        @Android.databinding.BindingAdapter(value = {"beforeTextChangedCommand", "onTextChangedCommand", "afterTextChangedCommand"}, requireAll = false)
        public static void editTextCommand(EditText editText,
        final ReplyCommand<TextChangeDataWrapper> beforeTextChangedCommand,
        final ReplyCommand<TextChangeDataWrapper> onTextChangedCommand,
        final ReplyCommand<String> afterTextChangedCommand) {
        editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (beforeTextChangedCommand != null) {
        beforeTextChangedCommand.execute(new TextChangeDataWrapper(s, start, count, count));
        }
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (onTextChangedCommand != null) {
        onTextChangedCommand.execute(new TextChangeDataWrapper(s, start, before, count));
        }
        }

        @Override
        public void afterTextChanged(Editable s) {
        if (afterTextChangedCommand != null) {
        afterTextChangedCommand.execute(s.toString());
        }
        }
        });
        }

        但没有执行到这里public final ReplyCommand<String> afterTextChangedCommand = new ReplyCommand<>()?不清楚为什么了
      • 8ca711e21739:在主页面点击item后,再详情页下拉刷新的时候返回,报的错
        java.lang.RuntimeException: Unable to pause activity {com.kelin.mvvmlight.zhihu/com.kelin.mvvmlight.zhihu.newsdetail.NewsDetailActivity}: rx.exceptions.OnErrorNotImplementedException: Sequence contains no elements
      • 8ca711e21739:RXjava2出了.这个库还会更新吗
      • 风风风筝:demo的这种写法,下拉刷新,会闪白屏,可以说说更好的处理方式么~
      • SIM卡丢失:赞kelin
        Kelin:@倪小福 。。
      • 桃子爸比:public final ItemView itemView = ItemView.of(BR.viewModel, R.layout.layout_message_item);

        viewModel一直标红是什么鬼啊。。。。
        桃子爸比:@Kelin 可以了 名字没有对应上:smile:
        Kelin:有时候会这样,能正常跑应该就没问题,File > Invalidate Caches/Restart试试:smile:
      • 03e314e484cd:请问下,我找了半天都没找到添加列表数据(listview,viewpater什么的)的adapter代码在哪....
        Kelin:@风平浪静明天 用BindingAdapter 实现了 在一个第三方库里面 你再找找
      • carlos23:Messenger 就是EventBus吧
        8ca711e21739:应该是rxbus
      • ChongmingLiu:作者你好,我遇到有一个地方不太懂,关于“ImageView自动去加载这个uri的图片”。
        请问给ImageView设完bind:uri之后就可以了吗?
        ChongmingLiu:@Kelin :clap: 是我的虚拟机有问题 清一下缓存就可以刷出来了
        Kelin:@ChongmingLiu 恩恩 设置uri就可以了,图片加载的事情已经帮你通过BindingAdapter做好了
      • Tomkk:那个用bind:uri="@{viewModel.imageUrl}"自动加载图片时,炸了,原因是运行到image/ViewBindingAdapter 里面的方法loadImage时,提示我
        ImagePipelineFactory was not initialized! 怎么处理这个问题啊! :sob:
      • Tomkk:在你的sample里面有一个FragmentNewListBinding, 是在Fragment里面的,我也想做一个Fragment,但是按照你的这个FragmentNewListBinding,并不能找到这个东西
        Kelin:@迈克尔科神 FragmentNewListBinding 是自动生成的,如果没有话,试着编译一下就会生成,如果xml布局里面没有写错东西的话
      • 3dc2660b084e:你好啊,我想问一下binding.setVariable(),里面的"BR"是什么,为什么我的会变红了
        3dc2660b084e:@我是一只咸鱼kk 弄好了 = =
      • _浮生若梦:请问,为什么我的项目中没有生成BR类,我的包名com.bjprd.zcgl,直接这样写com.bjprd.zcgl.BR.viewModel也能运行,但是在类上有红色的曲线错误提示
        Kelin:@浮生若梦6674 有时候会这样,能正常跑应该就没问题,File > Invalidate Caches/Restart试试
      • d13457a15d53:你好,我在你的教程贴里面回复了,可能你没看到。
        https://github.com/evant/binding-collection-adapter
        这个库有个bug,关于列表adapter绑定的,我给作者说了,他应该会更新,建议您也更新下他的库。你现在是1.1.0,他的新版已经是1.3.0了。
        Kelin:@Xx_xX 我们项目也是用依赖Module的方式,应该不会有问题,你可以不用Module的方式,既然你在他的源码里加了一个判断就能好,你就在你代码某个地方写一个和他一模一样的方法 @SuppressWarnings("unchecked")
        @BindingAdapter(value = { "itemView", "items", "adapter", "itemIds", "viewHolder" }, requireAll = false)
        public static < T > void setAdapter( RecyclerView recyclerView, ItemViewArg< T > arg, List< T > items, BindingRecyclerViewAdapterFactory factory, BindingRecyclerViewAdapter.ItemIds< T > itemIds, BindingRecyclerViewAdapter.ViewHolderFactory viewHolderFactory ) {}
        参数都不用变,虽然是static 方法,databinding框架在找的时候,参数匹配之后会优先使用你的代码,而不是他库里面的代码。
        d13457a15d53:@Kelin 他应该还没改。
        主要是Databinding本身库的BUG引起的,5.0以下,列表绑定会报异常
        https://code.google.com/p/android/issues/detail?id=220801
        可以看看这个;

        然后我在他的源码里加了一个判断

        ```
        @SuppressWarnings("unchecked")
        @BindingAdapter(value = { "itemView", "items", "adapter", "itemIds", "viewHolder" }, requireAll = false)
        public static < T > void setAdapter( RecyclerView recyclerView, ItemViewArg< T > arg, List< T > items, BindingRecyclerViewAdapterFactory factory, BindingRecyclerViewAdapter.ItemIds< T > itemIds, BindingRecyclerViewAdapter.ViewHolderFactory viewHolderFactory ) {
        if ( arg == null ) {
        throw new IllegalArgumentException( "itemView must not be null" );
        }
        if ( factory == null ) {
        factory = BindingRecyclerViewAdapterFactory.DEFAULT;
        }
        if ( arg.getItemView() != null ) {
        BindingRecyclerViewAdapter< T > adapter = ( BindingRecyclerViewAdapter< T > ) recyclerView.getAdapter();
        if ( adapter == null ) {
        adapter = factory.create( recyclerView, arg );
        adapter.setItems( items );
        adapter.setItemIds( itemIds );
        adapter.setViewHolderFactory( viewHolderFactory );
        recyclerView.setAdapter( adapter );
        } else {
        adapter.setItems( items );
        }
        }
        }
        ```

        这样可以避免那个问题,我还是想用你的这个库,但是我发我通过依赖Module的方式,在xml中用你的@BindAdapter没办法找到。很奇怪

        ```
        Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
        > java.lang.RuntimeException: Found data binding errors.
        ****/ data binding error ****msg:Cannot find the setter for attribute 'app:clickCommand' with parameter type com.kelin.mvvmlight.command.ReplyCommand on android.widget.TextView.
        file:D:\Code\RxAndroid\app\src\main\res\layout\layout_include.xml
        loc:24:32 - 24:60
        ****\ data binding error ****
        ```

        不知道能否帮忙解惑
        Kelin:@Xx_xX 可能是看到了,比较忙没回复,之后就没想起来,我再更新一下,谢谢反馈 :smile:
      • 北疆_:这真是吊炸天的节奏,必须顶一下
      • 捡淑:先马克
        _孑孓_:这头像~
        捡淑:@ITIan 简书马克流
        ITIan:@捡淑 为嘛经常看到你的头像 :smile: 有点逆天
      • Euterpe:imageview 加载可以更换Glide来加载吗?
        Euterpe:@七界无尘 恩 之前还没太熟悉Databinding
        303e0b7107b8:@Euterpe 用什么图片加载框架都是可以的,就像楼主说的需要用@Bindadapter配置好就,你也可以在此处配置Glide的全局加载动画等等,看了楼主的github给我之前项目里想得比较像,不过楼主封装的比较完整,赞一个
        Kelin:@Euterpe 不好意思,回的比较晚,如果想要实现自己的图片加载逻辑,要自己去用 @BindingAdapter去实现哦

      本文标题:MVVM Light Toolkit使用指南

      本文链接:https://www.haomeiwen.com/subject/hxdnrttx.html