Android 中ViewPager重排序与更新实例详解
Android中ViewPager重排序与更新实例详解
最近的项目中有栏目订阅功能,在更改栏目顺序以后需要更新ViewPager。类似于网易新闻的频道管理。
在重新排序之后调用了PagerAdapter的notifyDataSetChanged方法,发现ViewPager并没有更新,于是我开始跟踪源码,在调用PagerAdapter的notifyDataSetChanged方法后,会触发Viewpager的dataSetChanged方法。
voiddataSetChanged(){ //Thismethodonlygetscalledifourobserverisattached,somAdapterisnon-null. finalintadapterCount=mAdapter.getCount(); mExpectedAdapterCount=adapterCount; booleanneedPopulate=mItems.size()通过源码发现,在发生数据更新是,ViewPager会调用Adapter.getItemPosition判断当前页是否发生变化,如果当前页没有变化则返回POSITION_UNCHANGED,如果当前页的顺序发生变化则返回新的索引,如果当前页不存在则返回POSITION_NONE将会移除当前页并更新当前页。
接着查看ViewPagerAdapter的getItemPosition方法
publicintgetItemPosition(Objectobject){ returnPOSITION_UNCHANGED; }发现默认返回POSITION_UNCHANGED,这也是为什么我们的ViewPager没有更新的原因,网上有多种解决方案,其中一种是直接重写getItemPosition直接返回POSITION_NONE。我也试着使用了,发现并没有什么用,数据还是没有更新,后来发现我的Adapter继承的是FragmentPagerAdapter。而FragmentPagerAdapter自带了缓存策略,查看其instantiateItem方法。
@Override publicObjectinstantiateItem(ViewGroupcontainer,intposition){ if(mCurTransaction==null){ mCurTransaction=mFragmentManager.beginTransaction(); } finallongitemId=getItemId(position); //Dowealreadyhavethisfragment? Stringname=makeFragmentName(container.getId(),itemId); Fragmentfragment=mFragmentManager.findFragmentByTag(name); if(fragment!=null){ if(DEBUG)Log.v(TAG,"Attachingitem#"+itemId+":f="+fragment); mCurTransaction.attach(fragment); }else{ fragment=getItem(position); if(DEBUG)Log.v(TAG,"Addingitem#"+itemId+":f="+fragment); mCurTransaction.add(container.getId(),fragment, makeFragmentName(container.getId(),itemId)); } if(fragment!=mCurrentPrimaryItem){ fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } returnfragment; }我们可以发现FragmentPagerAdapter通过其内部的FragmentManager管理Fragment缓存,而每一个Fragment都是通过name来分别的,而name则由makeFragmentName生成,我们查看makeFragmentName方法
privatestaticStringmakeFragmentName(intviewId,longid){ return"android:switcher:"+viewId+":"+id; }很简单拼接的字符串,一个是Viewpager的id,一个是由getItemId方法生成,而getItemId方法更简单直接返回position,这也就是为什么我们不能更新数据的原因。
/** *Returnauniqueidentifierfortheitematthegivenposition. * *Thedefaultimplementationreturnsthegivenposition. *Subclassesshouldoverridethismethodifthepositionsofitemscanchange.
* *@parampositionPositionwithinthisadapter *@returnUniqueidentifierfortheitematposition */ publiclonggetItemId(intposition){ returnposition; }知道原因以后接着就开始改造Adapter,首先为每一个频道生成唯一的ID我的做法是使用一个Map来保存,频道名称与ID的对应关系,使用一个List来保存之前的Position顺序,记得在notifyDataSetChanged中初始化,由于List保存的是之前的Position所以需要在完成更新后,再添加。
intid=1; MapIdsMap=newHashMap<>(); List preIds=newArrayList<>(); @Override publicvoidnotifyDataSetChanged(){ for(MenuInfoinfo:data){ if(!IdsMap.containsKey(info.getTitle())){ IdsMap.put(info.getTitle(),id++); } } super.notifyDataSetChanged(); preIds.clear(); intsize=getCount(); for(inti=0;i 接着重写getItemPosition
@Override publicintgetItemPosition(Objectobject){ ItemFragmentfragment=(ItemFragment)object; Stringtitle=fragment.getTitle(); intpreId=preIds.indexOf(fragment.getTitle()); intnewId=-1; inti=0; intsize=getCount(); for(;i还有getItemId
@Override publiclonggetItemId(intposition){ returnIdsMap.get(getPageTitle(position)); }完整的代码
packagecom.trs.xizang.gov.adapter; importandroid.os.Bundle; importandroid.support.v4.app.Fragment; importandroid.support.v4.app.FragmentManager; importandroid.support.v4.app.FragmentPagerAdapter; importandroid.util.Log; importandroid.view.ViewGroup; importcom.trs.lib.base.TRSUrlFragment; importcom.trs.lib.bean.TRSMenu; importcom.trs.lib.fragment.base.SimpleTitleFragment; importcom.trs.xizang.gov.bean.MenuInfo; importcom.trs.xizang.gov.fragment.ItemFragment; importjava.util.ArrayList; importjava.util.HashMap; importjava.util.List; importjava.util.Map; /** *Createdbyzhuguohuion2016/5/12. */ publicclassMenuInfoPageAdapterextendsFragmentPagerAdapter{ Listdata; intid=1; Map IdsMap=newHashMap<>(); List preIds=newArrayList<>(); publicMenuInfoPageAdapter(FragmentManagermanager,List data){ super(manager); this.data=data==null?newArrayList ():data; } @Override publicintgetCount(){ returndata.size(); } @Override publicFragmentgetItem(intposition){ ItemFragmentfragment=newItemFragment(); Bundlebundle=newBundle(); bundle.putString(TRSUrlFragment.KEY_URL,data.get(position).getUrl()); bundle.putString(TRSUrlFragment.KEY_TITLE,data.get(position).getTitle()); fragment.setArguments(bundle); returnfragment; } @Override publicCharSequencegetPageTitle(intposition){ returndata.get(position).getTitle(); } @Override publicObjectinstantiateItem(ViewGroupcontainer,intposition){ returnsuper.instantiateItem(container,position); } @Override publiclonggetItemId(intposition){ returnIdsMap.get(getPageTitle(position)); } @Override publicintgetItemPosition(Objectobject){ ItemFragmentfragment=(ItemFragment)object; Stringtitle=fragment.getTitle(); intpreId=preIds.indexOf(fragment.getTitle()); intnewId=-1; inti=0; intsize=getCount(); for(;i 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!