Android 中ViewPager重排序與更新實(shí)例詳解
Android 中ViewPager重排序與更新實(shí)例詳解
最近的項(xiàng)目中有欄目訂閱功能,在更改欄目順序以后需要更新ViewPager。類似于網(wǎng)易新聞的頻道管理。
在重新排序之后調(diào)用了PagerAdapter的notifyDataSetChanged方法,發(fā)現(xiàn)ViewPager并沒(méi)有更新,于是我開始跟蹤源碼,在調(diào)用PagerAdapter的notifyDataSetChanged方法后,會(huì)觸發(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(); } }
通過(guò)源碼發(fā)現(xiàn),在發(fā)生數(shù)據(jù)更新是,ViewPager會(huì)調(diào)用Adapter.getItemPosition判斷當(dāng)前頁(yè)是否發(fā)生變化,如果當(dāng)前頁(yè)沒(méi)有變化則返回POSITION_UNCHANGED,如果當(dāng)前頁(yè)的順序發(fā)生變化則返回新的索引,如果當(dāng)前頁(yè)不存在則返回POSITION_NONE將會(huì)移除當(dāng)前頁(yè)并更新當(dāng)前頁(yè)。
接著查看ViewPagerAdapter的getItemPosition方法
public int getItemPosition(Object object) { return POSITION_UNCHANGED; }
發(fā)現(xiàn)默認(rèn)返回POSITION_UNCHANGED,這也是為什么我們的ViewPager沒(méi)有更新的原因,網(wǎng)上有多種解決方案,其中一種是直接重寫getItemPosition直接返回POSITION_NONE。我也試著使用了,發(fā)現(xiàn)并沒(méi)有什么用,數(shù)據(jù)還是沒(méi)有更新,后來(lái)發(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通過(guò)其內(nèi)部的FragmentManager管理Fragment緩存,而每一個(gè)Fragment都是通過(guò)name來(lái)分別的,而name則由makeFragmentName生成,我們查看makeFragmentName方法
private static String makeFragmentName(int viewId, long id) { return "android:switcher:" + viewId + ":" + id; }
很簡(jiǎn)單拼接的字符串,一個(gè)是Viewpager的id,一個(gè)是由getItemId方法生成,而getItemId方法更簡(jiǎn)單直接返回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,首先為每一個(gè)頻道生成唯一的ID我的做法是使用一個(gè)Map來(lái)保存,頻道名稱與ID的對(duì)應(yīng)關(guān)系,使用一個(gè)List來(lái)保存之前的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; }
還有g(shù)etItemId
@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)); } } }
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Android解決viewpager嵌套滑動(dòng)沖突并保留側(cè)滑菜單功能
- Android中DrawerLayout+ViewPager滑動(dòng)沖突的解決方法
- Android中TabLayout+ViewPager實(shí)現(xiàn)tab和頁(yè)面聯(lián)動(dòng)效果
- Android之禁止ViewPager滑動(dòng)實(shí)現(xiàn)實(shí)例
- Android 自定義布局豎向的ViewPager的實(shí)現(xiàn)
- Android使用TabLayou+fragment+viewpager實(shí)現(xiàn)滑動(dòng)切換頁(yè)面效果
- android 中viewpager+fragment仿微信底部TAG完美漸變
- Android ViewPager撤消左右滑動(dòng)切換功能實(shí)現(xiàn)代碼
相關(guān)文章
Android通過(guò)SeekBar調(diào)節(jié)布局背景顏色
這篇文章主要為大家詳細(xì)介紹了Android通過(guò)SeekBar調(diào)節(jié)布局背景顏色,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04Android WebView的使用方法及與JS 相互調(diào)用
這篇文章主要介紹了Android WebView的使用方法及與JS 相互調(diào)用的相關(guān)資料,WebView 是 Android 中一個(gè)非常實(shí)用的組​件, WebView 可以使得網(wǎng)頁(yè)輕松的內(nèi)嵌到app里,還可以直接跟js相互調(diào)用,需要的朋友可以參考下2017-07-07微信小程序 實(shí)現(xiàn)列表刷新的實(shí)例詳解
這篇文章主要介紹了微信小程序 實(shí)現(xiàn)列表刷新的實(shí)例詳解的相關(guān)資料,這里提供了實(shí)現(xiàn)代碼及實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-11-11Android編程實(shí)現(xiàn)ListView內(nèi)容無(wú)限循環(huán)顯示的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)ListView內(nèi)容無(wú)限循環(huán)顯示的方法,通過(guò)繼承Adapter類實(shí)現(xiàn)ListView中的數(shù)據(jù)無(wú)限循環(huán)顯示功能,需要的朋友可以參考下2017-06-06Android實(shí)現(xiàn)驗(yàn)證碼登錄
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)驗(yàn)證碼登錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03android中UIColletionView瀑布流布局實(shí)現(xiàn)思路以及封裝的實(shí)現(xiàn)
本篇文章主要介紹了android中UIColletionView瀑布流布局實(shí)現(xiàn)思路以及封裝的實(shí)現(xiàn),具有一定的參考價(jià)值,有興趣的可以了解一下。<BR>2017-02-02android實(shí)現(xiàn)密碼框右側(cè)顯示小眼睛
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)密碼框右側(cè)顯示小眼睛,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09Android系統(tǒng)開發(fā)中l(wèi)og的使用方法及簡(jiǎn)單的原理
LOG是廣泛使用的用來(lái)記錄程序執(zhí)行過(guò)程的機(jī)制,它既可以用于程序調(diào)試,也可以用于產(chǎn)品運(yùn)營(yíng)中的事件記錄;在平時(shí)開發(fā)過(guò)程中經(jīng)常需要與log打交道,所以很有必要了解log的使用方法及簡(jiǎn)單的原理,感興趣的朋友可以了解下啊2013-01-01