Работа с приложением, которое необходимо динамически добавлять и удалять фрагменты списка из 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?
Да, это уже было установлено значение true – ddog