1

Работа с приложением, которое необходимо динамически добавлять и удалять фрагменты списка из viewpager, при нажатии на элемент в списке новый фрагмент добавляет в viewpager и когда нужно удалить удаленный фрагмент, это работает хорошо, но когда я поворачиваю экран, я получаю двойные экземпляры фрагментов в пейджере, поворачиваю его снова, и он увеличивает число экземпляров в четыре раза.Восстановление фрагментов и их состояний в viewpager при вращении

В то же время мне нужно сохранить состояние фрагментов (позиция списка, количество загруженных предметов и т. Д.), Это означает, что он отменяет onsaveinstancestate в фрагментах и ​​сохраняет соответствующие данные в комплекте для восстановления на отдыхе. Мне удалось решить проблему двойных экземпляров, очистив адаптер и вызвав 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(); 
    } 
} 

и держатель POJO, который я использую, чтобы сохранить в onsaveinstancestate активности и восстановления на вращение

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

onsaveinstance способ в деятельности

@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?

ответ

0

Посмотрите на этих ресурсах:

How does viewPager retain fragment states on orientation change?

Fragment in ViewPager on Fragment doesn't reload on orientation change

https://stackoverflow.com/a/27316052/2930101

Попробуйте искать фрагментов в менеджере фрагмента первой, когда вы возвращаете их в ViewPager.

Если вам удастся решить вашу проблему, я буду заинтересован в вашем решении!

1

Можете ли вы попробовать использовать setRetainInstance (булево сохранить) в методе onCreateView вашего фрагмента. Установите его на true. Определяет, сохраняется ли экземпляр фрагмента в процессе повторного создания активности (например, при изменении конфигурации).

+0

Да, это уже было установлено значение true – ddog

0

мне удалось это исправить, отредактировав этот код

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

Проблема в том, что хэш-код генерируется и отличается от каждого поворота и поскольку требование не менять положение страниц я только что удалили Этот метод. Он работает сейчас, как и ожидалось, вы можете добавлять и удалять фрагменты с восстановлением состояния при изменении ориентации.

Смежные вопросы