全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

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


# Android  # 中ViewPager重排序  # ViewPager排序  # Android解决viewpager嵌套滑动冲突并保留侧滑菜单功能  # Android中DrawerLayout+ViewPager滑动冲突的解决方法  # Android中TabLayout+ViewPager实现tab和页面联动效果  # Android之禁止ViewPager滑动实现实例  # Android 自定义布局竖向的ViewPager的实现  # Android使用TabLayou+fragment+viewpager实现滑动切换页面效果  # android 中viewpager+fragment仿微信底部TAG完美渐变  # Android ViewPager撤消左右滑动切换功能实现代码  # 当前页  # 的是  # 重写  # 都是  # 我也  # 将会  # 是由  # 中有  # 我们可以  # 这也  # 希望能  # 很简单  # 不存在  # 带了  # 谢谢大家  # 试着  # 类似于  # 有多种  # 什么用  # 则由 


相关文章: c# 在高并发场景下,委托和接口调用的性能对比  C#怎么创建控制台应用 C# Console App项目创建方法  企业宣传片制作网站有哪些,传媒公司怎么找企业宣传片项目?  如何快速搭建高效简练网站?  建站主机如何选?性能与价格怎样平衡?  太原网站制作公司有哪些,网约车营运证查询官网?  东莞专业制作网站的公司,东莞大学生网的网址是什么?  专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?  在线流程图制作网站手机版,谁能推荐几个好的CG原画资源网站么?  C#如何序列化对象为XML XmlSerializer用法  如何快速上传自定义模板至建站之星?  公司网站制作费用多少,为公司建立一个网站需要哪些费用?  建站IDE高效指南:快速搭建+SEO优化+自适应模板全解析  香港服务器部署网站为何提示未备案?  西安制作网站公司有哪些,西安货运司机用的最多的app或者网站是什么?  北京网站制作的公司有哪些,北京白云观官方网站?  建站168自助建站系统:快速模板定制与SEO优化指南  电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?  免费制作海报的网站,哪位做平面的朋友告诉我用什么软件做海报比较好?ps还是cd还是ai这几个软件我都会些我是做网页的?  如何选择适配移动端的WAP自助建站平台?  如何通过VPS搭建网站快速盈利?  建站之星安装需要哪些步骤及注意事项?  建站之星价格显示格式升级,你的预算足够吗?  XML的“混合内容”是什么 怎么用DTD或XSD定义  如何快速搭建高效香港服务器网站?  5种Android数据存储方式汇总  企业网站制作费用多少,企业网站空间一般需要多大,费用是多少?  香港服务器网站卡顿?如何解决网络延迟与负载问题?  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  如何在阿里云域名上完成建站全流程?  微信推文制作网站有哪些,怎么做微信推文,急?  在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?  宝盒自助建站智能生成技巧:SEO优化与关键词设置指南  重庆网站制作公司哪家好,重庆中考招生办官方网站?  南京做网站制作公司,南京哈发网络有限公司,公司怎么样,做网页美工DIV+CSS待遇怎么样?  大型企业网站制作流程,做网站需要注册公司吗?  代购小票制作网站有哪些,购物小票的简要说明?  如何自定义建站之星网站的导航菜单样式?  阿里云高弹*务器配置方案|支持分布式架构与多节点部署  网站视频怎么制作,哪个网站可以免费收看好莱坞经典大片?  如何通过万网虚拟主机快速搭建网站?  h5网站制作工具有哪些,h5页面制作工具有哪些?  已有域名如何免费搭建网站?  学校免费自助建站系统:智能生成+拖拽设计+多端适配  建站之星导航配置指南:自助建站与SEO优化全解析  如何用美橙互联一键搭建多站合一网站?  ,在苏州找工作,上哪个网站比较好?  成都品牌网站制作公司,成都营业执照年报网上怎么办理?  ppt制作免费网站有哪些,ppt模板免费下载网站? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。