Android 中ViewPager重排序与更新实例详解
android 中viewpager重排序与更新实例详解
最近的项目中有栏目订阅功能,在更改栏目顺序以后需要更新viewpager。类似于网易新闻的频道管理。
在重新排序之后调用了pageradapter的notifydatasetchanged方法,发现viewpager并没有更新,于是我开始跟踪源码,在调用pageradapter的notifydatasetchanged方法后,会触发viewpager的datasetchanged方法。
void datasetchanged() { // this method only gets called if our observer is attached, so madapter is non-null. final int adaptercount = madapter.getcount(); mexpectedadaptercount = adaptercount; boolean needpopulate = mitems.size() < moffscreenpagelimit * 2 + 1 && mitems.size() < adaptercount; int newcurritem = mcuritem; boolean isupdating = false; for (int i = 0; i < mitems.size(); i++) { final iteminfo ii = mitems.get(i); final int newpos = 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) { // keep the current item in the valid range newcurritem = math.max(0, math.min(mcuritem, adaptercount - 1)); needpopulate = true; } continue; } if (ii.position != newpos) { if (ii.position == mcuritem) { // our current item changed position. follow it. newcurritem = newpos; } ii.position = newpos; needpopulate = true; } } if (isupdating) { madapter.finishupdate(this); } collections.sort(mitems, comparator); if (needpopulate) { // reset our known page widths; populate will recompute them. final int childcount = getchildcount(); for (int i = 0; i < childcount; i++) { final view child = getchildat(i); final layoutparams lp = (layoutparams) child.getlayoutparams(); if (!lp.isdecor) { lp.widthfactor = 0.f; } } setcurrentiteminternal(newcurritem, false, true); requestlayout(); } }
通过源码发现,在发生数据更新是,viewpager会调用adapter.getitemposition判断当前页是否发生变化,如果当前页没有变化则返回position_unchanged,如果当前页的顺序发生变化则返回新的索引,如果当前页不存在则返回position_none将会移除当前页并更新当前页。
接着查看viewpageradapter的getitemposition方法
public int getitemposition(object object) { return position_unchanged; }
发现默认返回position_unchanged,这也是为什么我们的viewpager没有更新的原因,网上有多种解决方案,其中一种是直接重写getitemposition直接返回position_none。我也试着使用了,发现并没有什么用,数据还是没有更新,后来发现我的adapter继承的是fragmentpageradapter。而fragmentpageradapter自带了缓存策略,查看其instantiateitem方法。
@override public object instantiateitem(viewgroup container, int position) { if (mcurtransaction == null) { mcurtransaction = mfragmentmanager.begintransaction(); } final long itemid = getitemid(position); // do we already have this fragment? string name = makefragmentname(container.getid(), itemid); fragment fragment = mfragmentmanager.findfragmentbytag(name); if (fragment != null) { if (debug) log.v(tag, "attaching item #" + itemid + ": f=" + fragment); mcurtransaction.attach(fragment); } else { fragment = getitem(position); if (debug) log.v(tag, "adding item #" + itemid + ": f=" + fragment); mcurtransaction.add(container.getid(), fragment, makefragmentname(container.getid(), itemid)); } if (fragment != mcurrentprimaryitem) { fragment.setmenuvisibility(false); fragment.setuservisiblehint(false); } return fragment; }
我们可以发现fragmentpageradapter通过其内部的fragmentmanager管理fragment缓存,而每一个fragment都是通过name来分别的,而name则由makefragmentname生成,我们查看makefragmentname方法
private static string makefragmentname(int viewid, long id) { return "android:switcher:" + viewid + ":" + id; }
很简单拼接的字符串,一个是viewpager的id,一个是由getitemid方法生成,而getitemid方法更简单直接返回position,这也就是为什么我们不能更新数据的原因。
/** * return a unique identifier for the item at the given position. * * <p>the default implementation returns the given position. * subclasses should override this method if the positions of items can change.</p> * * @param position position within this adapter * @return unique identifier for the item at position */ public long getitemid(int position) { return position; }
知道原因以后接着就开始改造adapter,首先为每一个频道生成唯一的id我的做法是使用一个map来保存,频道名称与id的对应关系,使用一个list来保存之前的position顺序,记得在notifydatasetchanged中初始化,由于list保存的是之前的position所以需要在完成更新后,再添加。
int id=1; map<string,integer> idsmap=new hashmap<>(); list<string> preids=new arraylist<>(); @override public void notifydatasetchanged() { for(menuinfo info:data){ if(!idsmap.containskey(info.gettitle())){ idsmap.put(info.gettitle(),id++); } } super.notifydatasetchanged(); preids.clear(); int size=getcount(); for(int i=0;i<size;i++){ preids.add((string) getpagetitle(i)); } }
接着重写getitemposition
@override public int getitemposition(object object) { itemfragment fragment= (itemfragment) object; string title=fragment.gettitle(); int preid = preids.indexof(fragment.gettitle()); int newid=-1; int i=0; int size=getcount(); for(;i<size;i++){ if(getpagetitle(i).equals(fragment.gettitle())){ newid=i; break; } } if(newid!=-1&&newid==preid){ log.i("zgh","title="+title+" position_unchanged"); return position_unchanged; } if(newid!=-1){ log.i("zgh","title="+title+" newid="+newid); return newid; } log.i("zgh","title="+title+" position_none"); return position_none; }
还有getitemid
@override public long getitemid(int position) { return idsmap.get(getpagetitle(position)); }
完整的代码
package com.trs.xizang.gov.adapter; import android.os.bundle; import android.support.v4.app.fragment; import android.support.v4.app.fragmentmanager; import android.support.v4.app.fragmentpageradapter; import android.util.log; import android.view.viewgroup; import com.trs.lib.base.trsurlfragment; import com.trs.lib.bean.trsmenu; import com.trs.lib.fragment.base.simpletitlefragment; import com.trs.xizang.gov.bean.menuinfo; import com.trs.xizang.gov.fragment.itemfragment; import java.util.arraylist; import java.util.hashmap; import java.util.list; import java.util.map; /** * created by zhuguohui on 2016/5/12. */ public class menuinfopageadapter extends fragmentpageradapter { list<menuinfo> data; int id=1; map<string,integer> idsmap=new hashmap<>(); list<string> preids=new arraylist<>(); public menuinfopageadapter(fragmentmanager manager, list<menuinfo> data){ super(manager); this.data= data==null? new arraylist<menuinfo>() :data; } @override public int getcount() { return data.size(); } @override public fragment getitem(int position) { itemfragment fragment=new itemfragment(); bundle bundle=new bundle(); bundle.putstring(trsurlfragment.key_url,data.get(position).geturl()); bundle.putstring(trsurlfragment.key_title, data.get(position).gettitle()); fragment.setarguments(bundle); return fragment; } @override public charsequence getpagetitle(int position) { return data.get(position).gettitle(); } @override public object instantiateitem(viewgroup container, int position) { return super.instantiateitem(container, position); } @override public long getitemid(int position) { return idsmap.get(getpagetitle(position)); } @override public int getitemposition(object object) { itemfragment fragment= (itemfragment) object; string title=fragment.gettitle(); int preid = preids.indexof(fragment.gettitle()); int newid=-1; int i=0; int size=getcount(); for(;i<size;i++){ if(getpagetitle(i).equals(fragment.gettitle())){ newid=i; break; } } if(newid!=-1&&newid==preid){ log.i("zgh","title="+title+" position_unchanged"); return position_unchanged; } if(newid!=-1){ log.i("zgh","title="+title+" newid="+newid); return newid; } log.i("zgh","title="+title+" position_none"); return position_none; } @override public void notifydatasetchanged() { for(menuinfo info:data){ if(!idsmap.containskey(info.gettitle())){ idsmap.put(info.gettitle(),id++); } } super.notifydatasetchanged(); preids.clear(); int size=getcount(); for(int i=0;i<size;i++){ preids.add((string) getpagetitle(i)); } } }
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
下一篇: Mcafee按访问扫描被禁用问题解决方法