1

處理需要動態添加和刪除viewpager中列表片段的應用程序時,點擊列表中的某個項目時,新片段將添加到viewpager,並且需要刪除向後滑動片段時,這工作良好,但是當我旋轉屏幕時,我在尋呼機中得到了碎片的雙重實例,再次旋轉它,它使實例翻了四倍。在viewpager循環中恢復片段及其狀態

同時我需要保持片段的狀態(列表位置,加載的項目數量等),這意味着覆蓋片段中的onsaveinstance狀態並保存捆綁中的相關數據以恢復娛樂。 我設法通過清除適配器並調用notifydatasetchanged來解決雙實例的問題,但之後我失去了在片段中的保存狀態,因爲onsaveinstance沒有被調用,原因很明顯,如果我沒有清除適配器,它只會使實例增加一倍。進入和離開文件夾時,我在Dropbox應用程序中看到了相同的行爲。

這是自定義的尋呼機適配器實現我使用

/** 
* Implementation of {@link PagerAdapter} that 
* uses a {@link Fragment} to manage each page. This class also handles 
* saving and restoring of fragment's state. 
* 
* <p>This version of the pager is more useful when there are a large number 
* of pages, working more like a list view. When pages are not visible to 
* the user, their entire fragment may be destroyed, only keeping the saved 
* state of that fragment. This allows the pager to hold on to much less 
* memory associated with each visited page as compared to 
* {@link FragmentPagerAdapter} at the cost of potentially more overhead when 
* switching between pages. 
* 
* <p>When using FragmentPagerAdapter the host ViewPager must have a 
* valid ID set.</p> 
* 
* <p>Subclasses only need to implement {@link #getItem(int)} 
* and {@link #getCount()} to have a working adapter. They also should 
* override {@link #getItemId(int)} if the position of the items can change. 
*/ 
public abstract class UpdatableFragmentPagerAdapter extends PagerAdapter { 

    private final FragmentManager fragmentManager; 
    private final LongSparseArray<Fragment> fragmentList = new LongSparseArray<>(); 
    private final LongSparseArray<Fragment.SavedState> savedStatesList = new LongSparseArray<>(); 
    @Nullable private FragmentTransaction currentTransaction = null; 
    @Nullable private Fragment currentPrimaryItem = null; 

    public UpdatableFragmentPagerAdapter(@NonNull FragmentManager fm) { 
    this.fragmentManager = fm; 
    } 

    /** 
    * Return the Fragment associated with a specified position. 
    */ 
    public abstract Fragment getItem(int position); 

    @Override public void startUpdate(@NonNull ViewGroup container) { 
    if (container.getId() == View.NO_ID) { 
     throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id"); 
    } 
    } 

    @Override @NonNull public Object instantiateItem(ViewGroup container, int position) { 
    long tag = getItemId(position); 
    Fragment fragment = fragmentList.get(tag); 
    // If we already have this item instantiated, there is nothing 
    // to do. This can happen when we are restoring the entire pager 
    // from its saved state, where the fragment manager has already 
    // taken care of restoring the fragments we previously had instantiated. 
    if (fragment != null) { 
     return fragment; 
    } 

    if (currentTransaction == null) { 
     currentTransaction = fragmentManager.beginTransaction(); 
    } 

    fragment = getItem(position); 
    // restore state 
    final Fragment.SavedState savedState = savedStatesList.get(tag); 
    if (savedState != null) { 
     fragment.setInitialSavedState(savedState); 
    } 
    fragment.setMenuVisibility(false); 
    fragment.setUserVisibleHint(false); 
    fragmentList.put(tag, fragment); 
    currentTransaction.add(container.getId(), fragment, "f" + tag); 

    return fragment; 
    } 

    @Override public void destroyItem(ViewGroup container, int position, @NonNull Object object) { 
    Fragment fragment = (Fragment) object; 
    int currentPosition = getItemPosition(fragment); 

    int index = fragmentList.indexOfValue(fragment); 
    long fragmentKey = -1; 
    if (index != -1) { 
     fragmentKey = fragmentList.keyAt(index); 
     fragmentList.removeAt(index); 
    } 

    //item hasn't been removed 
    if (fragment.isAdded() && currentPosition != POSITION_NONE) { 
     savedStatesList.put(fragmentKey, fragmentManager.saveFragmentInstanceState(fragment)); 
    } else { 
     savedStatesList.remove(fragmentKey); 
    } 

    if (currentTransaction == null) { 
     currentTransaction = fragmentManager.beginTransaction(); 
    } 

    currentTransaction.remove(fragment); 
    } 

    @Override public void setPrimaryItem(ViewGroup container, int position, @Nullable Object object) { 
    Fragment fragment = (Fragment) object; 
    if (fragment != currentPrimaryItem) { 
     if (currentPrimaryItem != null) { 
     currentPrimaryItem.setMenuVisibility(false); 
     currentPrimaryItem.setUserVisibleHint(false); 
     } 
     if (fragment != null) { 
     fragment.setMenuVisibility(true); 
     fragment.setUserVisibleHint(true); 
     } 
     currentPrimaryItem = fragment; 
    } 
    } 

    @Override public void finishUpdate(ViewGroup container) { 
    if (currentTransaction != null) { 
     currentTransaction.commitNowAllowingStateLoss(); 
     currentTransaction = null; 
    } 
    } 

    @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { 
    return ((Fragment) object).getView() == view; 
    } 

    @Override public Parcelable saveState() { 
    Bundle state = null; 
    if (savedStatesList.size() > 0) { 
     // save Fragment states 
     state = new Bundle(); 
     long[] stateIds = new long[savedStatesList.size()]; 
     for (int i = 0; i < savedStatesList.size(); i++) { 
     Fragment.SavedState entry = savedStatesList.valueAt(i); 
     stateIds[i] = savedStatesList.keyAt(i); 
     state.putParcelable(Long.toString(stateIds[i]), entry); 
     } 
     state.putLongArray("states", stateIds); 
    } 
    for (int i = 0; i < fragmentList.size(); i++) { 
     Fragment f = fragmentList.valueAt(i); 
     if (f != null && f.isAdded()) { 
     if (state == null) { 
      state = new Bundle(); 
     } 
     String key = "f" + fragmentList.keyAt(i); 
     fragmentManager.putFragment(state, key, f); 
     } 
    } 
    return state; 
    } 

    @Override public void restoreState(@Nullable Parcelable state, ClassLoader loader) { 
    if (state != null) { 
     Bundle bundle = (Bundle) state; 
     bundle.setClassLoader(loader); 
     long[] fss = bundle.getLongArray("states"); 
     savedStatesList.clear(); 
     fragmentList.clear(); 
     if (fss != null) { 
     for (long fs : fss) { 
      savedStatesList.put(fs, bundle.getParcelable(Long.toString(fs))); 
     } 
     } 
     Iterable<String> keys = bundle.keySet(); 
     for (String key : keys) { 
     if (key.startsWith("f")) { 
      Fragment f = fragmentManager.getFragment(bundle, key); 
      if (f != null) { 
      f.setMenuVisibility(false); 
      fragmentList.put(Long.parseLong(key.substring(1)), f); 
      } else { 
      Timber.w("Bad fragment at key %s", key); 
      } 
     } 
     } 
    } 
    } 

    /** 
    * Return a unique identifier for the item at the given position. 
    * <p> 
    * <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; 
    } 
} 

這是適配器

class FolderPagerAdapter extends UpdatableFragmentPagerAdapter { 

    private final FragmentManager fragmentManager; 
    // Sparse array to keep track of registered fragments in memory 
    private List<Fragment> addedFragments; 

    FolderPagerAdapter(FragmentManager fm) { 
    super(fm); 
    this.fragmentManager = fm; 
    } 

    void init() { 
    if (addedFragments == null) { 
     addedFragments = new ArrayList<>(); 
    } 
    addedFragments.clear(); 
    addedFragments.add(CollectionsListFragment.newInstance()); 
    notifyDataSetChanged(); 
    } 

    @Override public Fragment getItem(int position) { 
    return addedFragments.get(position); 
    } 

    @Override public long getItemId(int position) { 
    return addedFragments.get(position).hashCode(); 
    } 

    @Override public int getCount() { 
    return addedFragments.size(); 
    } 

    //this is called when notifyDataSetChanged() is called 
    @Override public int getItemPosition(Object object) { 
    //// refresh all fragments when data set changed 
    Fragment fragment = (Fragment) object; 
    if (fragment instanceof CollectionFragment) { 
     return POSITION_UNCHANGED; 
    } else { 
     int hashCode = fragment.hashCode(); 
     for (int i = 0; i < addedFragments.size(); i++) { 
     if (addedFragments.get(i).hashCode() == hashCode) { 
      return i; 
     } 
     } 
    } 
    return PagerAdapter.POSITION_NONE; 
    } 

    void removeLastPage() { 
    addedFragments.remove(addedFragments.size() - 1); 
    notifyDataSetChanged(); 
    } 

    void addCollectionFragment(CollectionFragment collectionFragment) { 
    addedFragments.add(collectionFragment); 
    notifyDataSetChanged(); 
    } 

    void addFolderFragment(FolderFragment folderFragment) { 
    addedFragments.add(folderFragment); 
    notifyDataSetChanged(); 
    } 

    void restoreFragments(List<PagerFolderCollectionModel> pagesList) { 
    if (!pagesList.isEmpty()) { 
     for (int i = 0; i < pagesList.size(); i++) { 
     if (i == 0) { 
      addedFragments.add(CollectionFragment.newInstance(pagesList.get(0).getItemId())); 
     } else { 
      addedFragments.add(FolderFragment.newInstance(pagesList.get(i).getItemName())); 
     } 
     } 
     notifyDataSetChanged(); 
    } 
    } 

    void removeAll() { 
    addedFragments.clear(); 
    notifyDataSetChanged(); 
    } 
} 

的實施和我正在使用的onSaveInstanceState保存在活動並恢復在支架POJO輪轉

public class PagerFolderCollectionModel implements Parcelable { 

    public static final Parcelable.Creator<PagerFolderCollectionModel> CREATOR = 
     new Parcelable.Creator<PagerFolderCollectionModel>() { 
     @Override public PagerFolderCollectionModel createFromParcel(Parcel source) { 
      return new PagerFolderCollectionModel(source); 
     } 

     @Override public PagerFolderCollectionModel[] newArray(int size) { 
      return new PagerFolderCollectionModel[size]; 
     } 
     }; 
    private String itemId; 
    private String itemName; 

    public PagerFolderCollectionModel(String itemId, String itemName) { 
    this.itemId = itemId; 
    this.itemName = itemName; 
    } 

    protected PagerFolderCollectionModel(Parcel in) { 
    this.itemId = in.readString(); 
    this.itemName = in.readString(); 
    } 

    public String getItemId() { 
    return itemId; 
    } 

    public String getItemName() { 
    return itemName; 
    } 

    @Override public int describeContents() { 
    return 0; 
    } 

    @Override public void writeToParcel(Parcel dest, int flags) { 
    dest.writeString(this.itemId); 
    dest.writeString(this.itemName); 
    } 
} 

onsave活動中的實例方法

@Override protected void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     outState.putInt(STATE_SELECTED_OPTION, selectedDrawerOption); 
     outState.putBoolean(STATE_SHOW_GRID_OPTION, isShowGridOption); 
     outState.putParcelableArrayList(STATE_SHOWN_FRAGMENTS, 
      (ArrayList<PagerFolderCollectionModel>) adapteritemslist); 
     Timber.e("save"); 
     } 

的要求是,在適配器中的第一項總是收集片段和文件夾片段添加點播(輕觸或滑動後)

取出是有這個(實施尋呼機適配器解決方案不同的是,在適配器中使用自定義視圖...)?有人知道這是如何在Dropbox應用程序中完成的嗎?

回答

1

能否請您嘗試使用 setRetainInstance(布爾保留) 在片段的onCreateView方法。將其設置爲true。 它控制是否在重新創建Activity(例如從配置更改)中保留片段實例。

+0

是的,這已經設置爲true – ddog

0

我已成功通過編輯代碼

@Override public long getItemId(int position) { 
    return addedFragments.get(position).hashCode(); 
} 

的問題是,哈希碼生成並將每次旋轉,並且由於要求是不改變頁面的位置我剛纔刪除不同的解決這個問題這種方法。它現在按預期工作,您可以在方向更改時添加和刪除具有恢復狀態的片段。