Viewpager在调用notifyDataSetChanged()时,界面无刷新
2016-01-22 14:12
375 查看
建议使用自己编译的androidos和虚拟机,这样就可以调试android系统中的任何组件。简单说来,深入android源码,去寻找解决问题的答案。这事儿说起来简单,实际做起来还是有些难度的。我也曾经尝试着去看过,没看一会儿就晕了。
所以还是有针对性的去看源码,效率会高一些。
废话不多说,先看第一个示例。
相信很多做过Viewpager的同学肯定遇到过这个问题,这个是bug还是android就是如此设计的,我们不做讨论。总之,它确实影响我们功能的实现了。
可能不少同学选择为Viewpager重新设置一遍适配器adapter,达到刷新的目的。但是这种方法在大多数情况下,是有问题的。
为什么调用数据更新的方法,Viewpager却没有更新呢,我们跟进该方法的源代码看一下。
首先查看适配器调用的super.notifyDataSetChanged(),该方法调到抽象基类PagerAdapter.notifyDataSetChanged()中:
注释里说到,当附加在适配器上的数据发生变化时,应该调用该方法刷新数据。该方法调用了一个mObservable.notifyChanged();
我们继续跟进这个方法,进入DataSetObservable类中,发现这样一段代码:
这都不是重点,重点我们来看这个mObservers的类型是一个抽象类DataSetObserver,里面只有两个未实现的方法,都有谁使用了这个抽象类呢,快捷键ctrl+alt+H,在众多的调用者当中,我们发现了Viewpager的身影
进入viewpager,我们终于找到了viewpager中控制数据变更的重点方法dataSetChanged,这个方法如下:
重点看这样一行代码:
官方对getItemPosition()的解释是:
Calledwhenthehostviewisattemptingtodetermineifanitem’spositionhaschanged.ReturnsPOSITION_UNCHANGEDiftheposition
ofthegivenitemhasnotchangedorPOSITION_NONEiftheitemisnolongerpresentintheadapter.
ThedefaultimplementationassumesthatitemswillneverchangepositionandalwaysreturnsPOSITION_UNCHANGED.
意思是如果item的位置如果没有发生变化,则返回POSITION_UNCHANGED。如果返回了POSITION_NONE,表示该位置的item已经不存在了。默认的实现是假设item的位置永远不会发生变化,而返回POSITION_UNCHANGED
所以我们可以尝试着修改适配器的写法,覆盖getItemPosition()方法,当调用notifyDataSetChanged时,让getItemPosition方法人为的返回POSITION_NONE,从而达到强迫viewpager重绘所有item的目的。
具体代码如下:
所以还是有针对性的去看源码,效率会高一些。
废话不多说,先看第一个示例。
Viewpager在调用notifyDataSetChanged()时,界面无刷新。
相信很多做过Viewpager的同学肯定遇到过这个问题,这个是bug还是android就是如此设计的,我们不做讨论。总之,它确实影响我们功能的实现了。可能不少同学选择为Viewpager重新设置一遍适配器adapter,达到刷新的目的。但是这种方法在大多数情况下,是有问题的。
追踪源代码:
为什么调用数据更新的方法,Viewpager却没有更新呢,我们跟进该方法的源代码看一下。首先查看适配器调用的super.notifyDataSetChanged(),该方法调到抽象基类PagerAdapter.notifyDataSetChanged()中:
/**
*Thismethodshouldbecalledbytheapplicationifthedatabackingthisadapterhaschanged
*andassociatedviewsshouldupdate.
*/
publicvoidnotifyDataSetChanged(){
mObservable.notifyChanged();
}
注释里说到,当附加在适配器上的数据发生变化时,应该调用该方法刷新数据。该方法调用了一个mObservable.notifyChanged();
我们继续跟进这个方法,进入DataSetObservable类中,发现这样一段代码:
/**
*Invokes{@linkDataSetObserver#onChanged}oneachobserver.
*Calledwhenthecontentsofthedatasethavechanged.Therecipient
*willobtainthenewcontentsthenexttimeitqueriesthedataset.
*/
publicvoidnotifyChanged(){
synchronized(mObservers){
//sinceonChanged()isimplementedbytheapp,itcoulddoanything,including
//removingitselffrom{@linkmObservers}-andthatcouldcauseproblemsif
//aniteratorisusedontheArrayList{@linkmObservers}.
//toavoidsuchproblems,justmarchthruthelistinthereverseorder.
for(inti=mObservers.size()-1;i>=0;i--){
mObservers.get(i).onChanged();
}
}
}
这都不是重点,重点我们来看这个mObservers的类型是一个抽象类DataSetObserver,里面只有两个未实现的方法,都有谁使用了这个抽象类呢,快捷键ctrl+alt+H,在众多的调用者当中,我们发现了Viewpager的身影
进入viewpager,我们终于找到了viewpager中控制数据变更的重点方法dataSetChanged,这个方法如下:
voiddataSetChanged(){
//Thismethodonlygetscalledifourobserverisattached,somAdapterisnon-null.
booleanneedPopulate=mItems.size()<mOffscreenPageLimit*2+1&&
mItems.size()<mAdapter.getCount();
intnewCurrItem=mCurItem;
booleanisUpdating=false;
for(inti=0;i<mItems.size();i++){
finalItemInfoii=mItems.get(i);
finalintnewPos=mAdapter.getItemPosition(ii.object);
if(newPos==PagerAdapter.POSITION_UNCHANGED){
continue;
}
if(newPos==PagerAdapter.POSITION_NONE){
mItems.remove(i);
i--;
if(!isUpdating){
mAdapter.startUpdate(this);
isUpdating=true;
}
mAdapter.destroyItem(this,ii.position,ii.object);
needPopulate=true;
if(mCurItem==ii.position){
//Keepthecurrentiteminthevalidrange
newCurrItem=Math.max(0,Math.min(mCurItem,mAdapter.getCount()-1));
needPopulate=true;
}
continue;
}
if(ii.position!=newPos){
if(ii.position==mCurItem){
//Ourcurrentitemchangedposition.Followit.
newCurrItem=newPos;
}
ii.position=newPos;
needPopulate=true;
}
}
if(isUpdating){
mAdapter.finishUpdate(this);
}
Collections.sort(mItems,COMPARATOR);
if(needPopulate){
//Resetourknownpagewidths;populatewillrecomputethem.
finalintchildCount=getChildCount();
for(inti=0;i<childCount;i++){
finalViewchild=getChildAt(i);
finalLayoutParamslp=(LayoutParams)child.getLayoutParams();
if(!lp.isDecor){
lp.widthFactor=0.f;
}
}
setCurrentItemInternal(newCurrItem,false,true);
requestLayout();
}
}
重点看这样一行代码:
finalintnewPos=mAdapter.getItemPosition(ii.object);
if(newPos==PagerAdapter.POSITION_UNCHANGED){
continue;
}
官方对getItemPosition()的解释是:
Calledwhenthehostviewisattemptingtodetermineifanitem’spositionhaschanged.ReturnsPOSITION_UNCHANGEDiftheposition
ofthegivenitemhasnotchangedorPOSITION_NONEiftheitemisnolongerpresentintheadapter.
ThedefaultimplementationassumesthatitemswillneverchangepositionandalwaysreturnsPOSITION_UNCHANGED.
意思是如果item的位置如果没有发生变化,则返回POSITION_UNCHANGED。如果返回了POSITION_NONE,表示该位置的item已经不存在了。默认的实现是假设item的位置永远不会发生变化,而返回POSITION_UNCHANGED
解决方案:
所以我们可以尝试着修改适配器的写法,覆盖getItemPosition()方法,当调用notifyDataSetChanged时,让getItemPosition方法人为的返回POSITION_NONE,从而达到强迫viewpager重绘所有item的目的。具体代码如下:
classSearchAdapterextendsPagerAdapter{
privateintmChildCount=0;
@Override
publicvoidnotifyDataSetChanged(){
mChildCount=getCount();
super.notifyDataSetChanged();
}
@Override
publicintgetItemPosition(Objectobject){
if(mChildCount>0){
mChildCount--;
returnPOSITION_NONE;
}
returnsuper.getItemPosition(object);
}
}
相关文章推荐
- VC小技巧20个
- 高级编程之线程(二)
- /Date(****)/处理成中国时间
- iOS 开发使用小细节
- sqllite命令简单方法使用
- 假设检验
- 如何解决Github上的 xxx.h文件not found
- EF MySql:Specified key was too long; max key length is 767 bytes解决方案
- Swift基础--手势识别(双击、捏、旋转、拖动、划动、长按)
- C语言 百炼成钢8
- python实现的直接插入排序算法
- Maven实战(二)构建简单Maven项目
- poj 3080 Blue Jeans KMP多模式匹配
- Laravel 之October Pages
- C#中增加弹出对话框控件的方法
- GET和POST解析
- Python 高级特性之列表生成式
- 高级编程之线程(一)
- 非常完善的Log4net详细说明
- 企业内刊、纸质杂志怎么转型?5分钟解决数字化难题