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撤消左右滑动切换功能实现代码
# 当前页
# 的是
# 重写
# 都是
# 我也
# 将会
# 是由
# 中有
# 我们可以
# 这也
# 希望能
# 很简单
# 不存在
# 带了
# 谢谢大家
# 试着
# 类似于
# 有多种
# 什么用
# 则由
相关文章:
小型网站建站如何选择虚拟主机?
如何高效搭建专业期货交易平台网站?
建站之星备案是否影响网站上线时间?
建站之星ASP如何实现CMS高效搭建与安全管理?
免费ppt制作网站,有没有值得推荐的免费PPT网站?
魔毅自助建站系统:模板定制与SEO优化一键生成指南
济南网站制作的价格,历城一职专官方网站?
上海网站制作网页,上海本地的生活网站有哪些?最好包括生活的各个方面的?
哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?
如何挑选优质建站一级代理提升网站排名?
网站制作壁纸教程视频,电脑壁纸网站?
建站之星展会模板:智能建站与自助搭建高效解决方案
如何选择可靠的免备案建站服务器?
制作公司内部网站有哪些,内网如何建网站?
建站之星CMS五站合一模板配置与SEO优化指南
淘宝制作网站有哪些,淘宝网官网主页?
北京专业网站制作设计师招聘,北京白云观官方网站?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
如何在IIS7上新建站点并设置安全权限?
如何快速启动建站代理加盟业务?
大连网站设计制作招聘信息,大连投诉网站有哪些?
如何配置IIS站点权限与局域网访问?
电商平台网站制作流程,电商网站如何制作?
新网站制作渠道有哪些,跪求一个无线渠道比较强的小说网站,我要发表小说?
如何通过商城免费建站系统源码自定义网站主题?
活动邀请函制作网站有哪些,活动邀请函文案?
企业在线网站设计制作流程,想建设一个属于自己的企业网站,该如何去做?
整人网站在线制作软件,整蛊网站退不出去必须要打我是白痴才能出去?
建站主机如何选?高性价比方案全解析
实现点击下箭头变上箭头来回切换的两种方法【推荐】
中山网站制作网页,中山新生登记系统登记流程?
如何确保FTP站点访问权限与数据传输安全?
南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?
天津个人网站制作公司,天津网约车驾驶员从业资格证官网?
如何通过网站建站时间优化SEO与用户体验?
如何在宝塔面板创建新站点?
小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建
Python如何创建带属性的XML节点
如何续费美橙建站之星域名及服务?
微信推文制作网站有哪些,怎么做微信推文,急?
如何设置并定期更换建站之星安全管理员密码?
如何在IIS中配置站点IP、端口及主机头?
交易网站制作流程,我想开通一个网站,注册一个交易网址,需要那些手续?
c# 服务器GC和工作站GC的区别和设置
如何规划企业建站流程的关键步骤?
内网网站制作软件,内网的网站如何发布到外网?
建站主机功能解析:服务器选择与快速搭建指南
外汇网站制作流程,如何在工商银行网站上做外汇买卖?
保定网站制作方案定制,保定招聘的渠道有哪些?找工作的人一般都去哪里看招聘信息?
C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)
*请认真填写需求信息,我们会在24小时内与您取得联系。