中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android 中ViewPager重排序與更新實例詳解

發布時間:2020-10-15 18:27:09 來源:腳本之家 閱讀:238 作者:lqh 欄目:移動開發

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

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

卓尼县| 桂林市| 松江区| 剑河县| 子洲县| 临高县| 丁青县| 民权县| 盐城市| 皋兰县| 湖北省| 彰化市| 永丰县| 祁连县| 泗阳县| 墨脱县| 财经| 资溪县| 洞口县| 尖扎县| 兴安盟| 柳江县| 潼南县| 沅陵县| 武宣县| 无锡市| 罗源县| 鄂托克前旗| 永城市| 鄂托克旗| 朝阳市| 龙江县| 剑川县| 都昌县| 浮山县| 大邑县| 交口县| 醴陵市| 台湾省| 临朐县| 东阳市|