当前位置: 移动技术网 > IT编程>移动开发>Android > Android 中ViewPager重排序与更新实例详解

Android 中ViewPager重排序与更新实例详解

2019年07月24日  | 移动技术网IT编程  | 我要评论

兽性大发之为虎作娼,都市炎黄,2015年mama音乐盛典

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));
    }
  }
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网