Android 中ViewPager重排序與更新實例詳解
Android 中ViewPager重排序與更新實例詳解
最近的項目中有欄目訂閱功能,在更改欄目順序以后需要更新ViewPager。類似于網(wǎng)易新聞的頻道管理。
在重新排序之后調(diào)用了PagerAdapter的notifyDataSetChanged方法,發(fā)現(xiàn)ViewPager并沒有更新,于是我開始跟蹤源碼,在調(diào)用PagerAdapter的notifyDataSetChanged方法后,會觸發(fā)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();
}
}
通過源碼發(fā)現(xiàn),在發(fā)生數(shù)據(jù)更新是,ViewPager會調(diào)用Adapter.getItemPosition判斷當前頁是否發(fā)生變化,如果當前頁沒有變化則返回POSITION_UNCHANGED,如果當前頁的順序發(fā)生變化則返回新的索引,如果當前頁不存在則返回POSITION_NONE將會移除當前頁并更新當前頁。
接著查看ViewPagerAdapter的getItemPosition方法
public int getItemPosition(Object object) {
return POSITION_UNCHANGED;
}
發(fā)現(xiàn)默認返回POSITION_UNCHANGED,這也是為什么我們的ViewPager沒有更新的原因,網(wǎng)上有多種解決方案,其中一種是直接重寫getItemPosition直接返回POSITION_NONE。我也試著使用了,發(fā)現(xiàn)并沒有什么用,數(shù)據(jù)還是沒有更新,后來發(fā)現(xiàn)我的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;
}
我們可以發(fā)現(xiàn)FragmentPagerAdapter通過其內(nèi)部的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,這也就是為什么我們不能更新數(shù)據(jù)的原因。
/**
* 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嵌套滑動沖突并保留側(cè)滑菜單功能
- Android中DrawerLayout+ViewPager滑動沖突的解決方法
- Android中TabLayout+ViewPager實現(xiàn)tab和頁面聯(lián)動效果
- Android之禁止ViewPager滑動實現(xiàn)實例
- Android 自定義布局豎向的ViewPager的實現(xiàn)
- Android使用TabLayou+fragment+viewpager實現(xiàn)滑動切換頁面效果
- android 中viewpager+fragment仿微信底部TAG完美漸變
- Android ViewPager撤消左右滑動切換功能實現(xiàn)代碼
相關文章
Android studio gradle環(huán)境變量配置教程
這篇文章主要為大家詳細介紹了Android studio gradle環(huán)境變量配置教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
Android使用glide加載gif動畫設置播放次數(shù)
這篇文章主要為大家詳細介紹了Android使用glide加載gif動畫設置播放次數(shù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11
Android 自定義gradle property詳解及實例代碼
這篇文章主要介紹了Android 自定義gradle property詳解及實例代碼的相關資料,需要的朋友可以參考下2017-02-02
Android 自動補全提示輸入AutoCompleteTextView、 MultiAutoCompleteTextV
本文主要介紹了Android自動補全提示輸入AutoCompleteTextView、 MultiAutoCompleteTextView,具有一定的參考作用,下面跟著小編一起來看下吧2017-01-01

