开户送体验金的老虎机网址:
无需注册送白菜彩金注册:
小结:
所以说,DiffUtil不仅仅只能和RecyclerView配合,我们也可以自己实现ListUpdateCallback接口的四个方法去做一些事情。(我暂时不负责任随便一项想,想到可以配合自己项目里的九宫格控件?或者优化我上篇文章写的NestFullListView?小安利,见 ListView、RecyclerView、ScrollView里嵌套ListView 相对优雅的解决方案:http://blog.csdn.net/zxt0601/article/details/52494665)
至此,我们已进化成文艺青年,运行效果和第一节图二基本一致,唯一不同的是此时adapter.notifyItemRangeChanged()会有Item白光一闪的更新动画(本文Demo的postion为0的item)。 这个Item一闪的动画有人喜欢有人恨,不过都不重要了,因为当我们学会了第三节的DiffUtil搞基用法,你爱不爱这个ItemChange动画,它都将随风而去。(不知道是不是官方bug)效果就是第一节的图二,我们的item0其实图片和文字都变化了,但是这个改变并没有伴随任何动画。让我们迈向 文艺青年中的文艺青年 之路。三 DiffUtil的高级用法理论:
三 DiffUtil的高级用法理论:
高级用法只涉及到两个方法,我们需要分别实现DiffUtil.Callback的public Object getChangePayload(int oldItemPosition, int newItemPosition)方法,返回的Object就是表示Item改变了哪些内容。
再配合RecyclerView.Adapter的public void onBindViewHolder(VH holder, int position, List<Object> payloads)方法,完成定向刷新。(成为文青中的文青,文青青。)敲黑板,这是一个新方法,注意它有三个参数,前两个我们熟,第三个参数就包含了我们在getChangePayload()返回的Object。
好吧,那我们就先看看这个方法是何方神圣:在v7-24.2.0的源码里,它长这个样子:
/**
*CalledbyRecyclerViewtodisplaythedataatthespecifiedposition.Thismethod
*shouldupdatethecontentsofthe{@linkViewHolder#itemView}toreflecttheitemat
*thegivenposition.
*<p>
*Notethatunlike{@linkandroid.widget.ListView},RecyclerViewwillnotcallthismethod
*againifthepositionoftheitemchangesinthedatasetunlesstheitemitselfis
*invalidatedorthenewpositioncannotbedetermined.Forthisreason,youshouldonly
*usethe<code>position</code>parameterwhileacquiringtherelateddataiteminside
*thismethodandshouldnotkeepacopyofit.Ifyouneedthepositionofanitemlater
*on(e.g.inaclicklistener),use{@linkViewHolder#getAdapterPosition()}whichwill
*havetheupdatedadapterposition.
*<p>
*Partialbindvsfullbind:
*<p>
*Thepayloadsparameterisamergelistfrom{@link#notifyItemChanged(int,Object)}or
*{@link#notifyItemRangeChanged(int,int,Object)}.Ifthepayloadslistisnotempty,
*theViewHolderiscurrentlyboundtoolddataandAdaptermayrunanefficientpartial
*updateusingthepayloadinfo.Ifthepayloadisempty,Adaptermustrunafullbind.
*Adaptershouldnotassumethatthepayloadpassedinnotifymethodswillbereceivedby
*onBindViewHolder().Forexamplewhentheviewisnotattachedtothescreen,the
*payloadinnotifyItemChange()willbesimplydropped.
*
*@paramholderTheViewHolderwhichshouldbeupdatedtorepresentthecontentsofthe
*itematthegivenpositioninthedataset.
*@parampositionThepositionoftheitemwithintheadapter'sdataset.
*@parampayloadsAnon-nulllistofmergedpayloads.Canbeemptylistifrequiresfull
*update.
*/
publicvoidonBindViewHolder(VHholder,intposition,List<Object>payloads){
onBindViewHolder(holder,position);
}
原来它内部就仅仅调用了两个参数的onBindViewHolder(holder, position),(题外话,哎哟喂,我的NestFullListView 的Adapter也有几分神似这种写法,看来我离Google大神又近了一步)看到这我才明白,其实onBind的入口,就是这个方法,它才是和onCreateViewHolder对应的方法,源码往下翻几行可以看到有个public final void bindViewHolder(VH holder, int position),它内部调用了三参的onBindViewHolder。关于RecyclerView.Adapter 也不是三言两句说的清楚的。(其实我只掌握到这里)好了不再跑题,回到我们的三参数的onBindViewHolder(VH holder, int position, List<Object> payloads),这个方法头部有一大堆英文注释,我一直觉得阅读这些英文注释对理解方法很有用处,于是我翻译了一下,
翻译:
由RecyclerView调用 用来在在指定的位置显示数据。这个方法应该更新ViewHolder里的ItemView的内容,以反映在给定的位置 Item(的变化)。请注意,不像ListView,如果给定位置的item的数据集变化了,RecyclerView不会再次调用这个方法,除非item本身失效了(invalidated ) 或者新的位置不能确定。出于这个原因,在这个方法里,你应该只使用 postion参数 去获取相关的数据item,而且不应该去保持 这个数据item的副本。如果你稍后需要这个item的position,例如设置clickListener。应该使用ViewHolder.getAdapterPosition(),它能提供 更新后的位置。(二笔的我看到这里发现 这是在讲解两参的onbindViewHolder方法下面是这个三参方法的独特部分:)**部分(partial)绑定**vs完整(full)绑定payloads 参数 是一个从(notifyItemChanged(int, Object)或notifyItemRangeChanged(int, int, Object))里得到的合并list。如果payloads list 不为空,那么当前绑定了旧数据的ViewHolder 和Adapter, 可以使用 payload的数据进行一次 高效的部分更新。如果payload 是空的,Adapter必须进行一次完整绑定(调用两参方法)。Adapter不应该假定(想当然的认为) 在那些notifyxxxx通知方法传递过来的payload, 一定会在 onBindViewHolder()方法里收到。(这一句翻译不好 QAQ 看举例就好)举例来说,当View没有attached 在屏幕上时,这个来自notifyItemChange()的payload 就简单的丢掉好了。payloads对象不会为null,但是它可能是空(empty),这时候需要完整绑定(所以我们在方法里只要判断isEmpty就好,不用重复判空)。作者语:这方法是一个高效的方法。 我是个低效的翻译者,我看了40+分钟。才终于明白,重要的部分已经加粗显示。
实战:
说了这么多话,其实用起来超级简单:先看如何使用getChangePayload()方法,又附带了中英双语注释
/**
*When{@link#areItemsTheSame(int,int)}returns{@codetrue}fortwoitemsand
*{@link#areContentsTheSame(int,int)}returnsfalseforthem,DiffUtil
*callsthismethodtogetapayloadaboutthechange.
*
*当{@link#areItemsTheSame(int,int)}返回true,且{@link#areContentsTheSame(int,int)}返回false时,DiffUtils会回调此方法,
*去得到这个Item(有哪些)改变的payload。
*
*Forexample,ifyouareusingDiffUtilwith{@linkRecyclerView},youcanreturnthe
*particularfieldthatchangedintheitemandyour
*{@linkandroid.support.v7.widget.RecyclerView.ItemAnimatorItemAnimator}canusethat
*informationtorunthecorrectanimation.
*
*例如,如果你用RecyclerView配合DiffUtils,你可以返回这个Item改变的那些字段,
*{@linkandroid.support.v7.widget.RecyclerView.ItemAnimatorItemAnimator}可以用那些信息去执行正确的动画
*
*Defaultimplementationreturns{@codenull}.\
*默认的实现是返回null
*
*@paramoldItemPositionThepositionoftheitemintheoldlist
*@paramnewItemPositionThepositionoftheiteminthenewlist
*@returnApayloadobjectthatrepresentsthechangebetweenthetwoitems.
*返回一个代表着新老item的改变内容的payload对象,
*/
@Nullable
@Override
publicObjectgetChangePayload(intoldItemPosition,intnewItemPosition){
//实现这个方法就能成为文艺青年中的文艺青年
//定向刷新中的部分更新
//效率最高
//只是没有了ItemChange的白光一闪动画,(反正我也觉得不太重要)
TestBeanoldBean=mOldDatas.get(oldItemPosition);
TestBeannewBean=mNewDatas.get(newItemPosition);
//这里就不用比较核心字段了,一定相等
Bundlepayload=newBundle();
if(!oldBean.getDesc().equals(newBean.getDesc())){
payload.putString("KEY_DESC",newBean.getDesc());
}
if(oldBean.getPic()!=newBean.getPic()){
payload.putInt("KEY_PIC",newBean.getPic());
}
if(payload.size()==0)//如果没有变化就传空
returnnull;
returnpayload;//
}
简单的说,这个方法返回一个Object类型的payload,它包含了某个item的变化了的那些内容。我们这里使用Bundle保存这些变化。
在Adapter里如下重写三参的onBindViewHolder:
@Override
publicvoidonBindViewHolder(DiffVHholder,intposition,List<Object>payloads){
if(payloads.isEmpty()){
onBindViewHolder(holder,position);
}else{
//文艺青年中的文青
Bundlepayload=(Bundle)payloads.get(0);
TestBeanbean=mDatas.get(position);
for(Stringkey:payload.keySet()){
switch(key){
case"KEY_DESC":
//这里可以用payload里的数据,不过data也是新的也可以用
holder.tv2.setText(bean.getDesc());
break;
case"KEY_PIC":
holder.iv.setImageResource(payload.getInt(key));
break;
default:
break;
}
}
}
}
这里传递过来的payloads是一个List,由注释可知,一定不为null,所以我们判断是否是empty,如果是empty,就调用两参的函数,进行一次Full Bind。如果不是empty,就进行partial bind,通过下标0取出我们在getChangePayload方法里返回的payload,然后遍历payload的key,根据key检索,如果payload里携带有相应的改变,就取出来 然后更新在ItemView上。(这里,通过mDatas获得的也是最新数据源的数据,所以用payload的数据或者新数据的数据 进行更新都可以)至此,我们已经掌握了刷新RecyclerView,文艺青年中最文艺的那种写法。
四 在子线程中使用DiffUtil
在DiffUtil的源码头部注释中介绍了DiffUtil的相关信息,DiffUtil内部采用的Eugene W. Myers’s difference 算法,但该算法不能检测移动的item,所以Google在其基础上改进支持检测移动项目,但是检测移动项目,会更耗性能。在有1000项数据,200处改动时,这个算法的耗时:打开了移动检测时:平均值:27.07ms,中位数:26.92ms。关闭了移动检测时:平均值:13.54ms,中位数:13.36ms。有兴趣可以自行去源码头部阅读注释,对我们比较有用的是其中一段提到,如果我们的list过大,这个计算出DiffResult的时间还是蛮久的,所以我们应该将获取DiffResult的过程放到子线程中,并在主线程中更新RecyclerView。这里我采用Handler配合DiffUtil使用:代码如下:
privatestaticfinalintH_CODE_UPDATE=1;
privateList<TestBean>mNewDatas;//增加一个变量暂存newList
privateHandlermHandler=newHandler(){
@Override
publicvoidhandleMessage(Messagemsg){
switch(msg.what){
caseH_CODE_UPDATE:
//取出Result
DiffUtil.DiffResultdiffResult=(DiffUtil.DiffResult)msg.obj;
diffResult.dispatchUpdatesTo(mAdapter);
//别忘了将新数据给Adapter
mDatas=mNewDatas;
mAdapter.setDatas(mDatas);
break;
}
}
};
newThread(newRunnable(){
@Override
publicvoidrun(){
//放在子线程中计算DiffResult
DiffUtil.DiffResultdiffResult=DiffUtil.calculateDiff(newDiffCallBack(mDatas,mNewDatas),true);
Messagemessage=mHandler.obtainMessage(H_CODE_UPDATE);
message.obj=diffResult;//obj存放DiffResult
message.sendToTarget();
}
}).start();
就是简单的Handler使用,不再赘述。
五总结和其他
1 其实本文代码量很少,可下载Demo查看,一共就四个类。但是不知不觉又被我写的这么长,主要涉及到了一些源码的注释的翻译,方便大家更好的理解。
2 DiffUtil很适合下拉刷新这种场景,更新的效率提高了,而且带动画,而且~还不用你动脑子算了。不过若是就做个删除 点赞这种,完全不用DiffUtils。自己记好postion,判断一下postion在不在屏幕里,调用那几个定向刷新的方法即可。
3 其实DiffUtil不是只能和RecyclerView.Adapter配合使用,我们可以自己实现 ListUpdateCallback接口,利用DIffUtil帮我们找到新旧数据集的最小差异集 来做更多的事情。
4 注意 写DEMO的时候,用于比较的新老数据集,不仅ArrayList不同,里面每个data也要不同。 否则changed 无法触发。实际项目中遇不到,因为新数据往往是网络来的。
5 今天是中秋节的最后一天,我们公司居然就开始上班了!!!气愤之余,我怒码一篇DiffUtil,我都不需要用DiffUtil,也能轻易比较出我们公司和其他公司的差异。QAQ,而且今天状态不佳,居然写了8个小时才完工。本以为这篇文章是可以入选微作文集的,没想到也是蛮长的。没有耐心的其实可以下载DEMO看看,代码量没多少,使用起来还是很轻松的。
6 关于“白光一闪”onChange动画,public Object getChangePayload()这个方法返回不为null的话,onChange采用Partial bind,就不会出现。 反之就有。
github传送门:好用给个star呗https://github.com/mcxtzhang/DiffUtils
CSDN传送门:http://download.csdn.net/detail/zxt0601/9632159
微信上福彩金豆博猫开户
老虎机注册就送奖金
免费自动彩金
博彩新会员送体验金