fragment更新数据后使用notifyDataSetChanged()无效
2016-10-18 16:39
246 查看
页面是根据网络数据返回的list数据动态生成的几个fragment构成的
修改包含fragment的集合数据
Viewpager在调用notifyDataSetChanged()时,界面无刷新
相信很多做过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的目的。
具体代码如下:
修改包含fragment的集合数据
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);
}
}
相关文章推荐
- 使用replace函数更新数据类型ntext无效怎么办
- FragMent和RadioButton 使用hide、show数据更新
- 使用viewpager+fragment,在activity启动模式为singleTask,跳转到当前页面重新加载数据fragment数据不更新
- JDBC使用ResultSet卷动、更新数据
- Android Fragment 数据动态更新的问题
- (转)PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理
- JDBC--使用statement接口实现更新数据
- Fragment基本使用(三)——与Activity之间传递数据
- SQL Update实现使用一个表的数据更新另一张表
- fragment与activity之间的回调,用于数据更新操作(是fragment向activity里传值)
- RecyclerView使用,RecyclerView使用,增加一个item,删除一个item,更新数据,item点击事件
- 使用oracle 闪回查询找回误更新的数据
- 支持“XX”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库
- 使用php+swoole对client数据实时更新
- 解决适配器(Adapter)数据更新调用 notifyDataSetChanged()无效问题
- Excel中添加并使用宏实现批量更新数据
- Fragment使用的细节,切换与交互数据
- 译文:使用MVC5的Entity Framework 6入门(八)——为ASP.NET MVC应用程序更新相关数据
- Fragment 中嵌套Fragment,更新数据,UI
- Oracle 使用MERGE INTO 语句更新数据