Skip to content

Instantly share code, notes, and snippets.

@technoir42
Last active December 20, 2016 14:23
Show Gist options
  • Save technoir42/91d1b9e73fbfa145bb32b713f8eef338 to your computer and use it in GitHub Desktop.
Save technoir42/91d1b9e73fbfa145bb32b713f8eef338 to your computer and use it in GitHub Desktop.
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.util.LongSparseArray;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.HashSet;
import java.util.Set;
/**
* An implementation of {@link PagerAdapter} similar to {@link android.support.v4.app.FragmentStatePagerAdapter}
* which items can be changed dynamically.
*/
public abstract class DynamicFragmentStatePagerAdapter extends PagerAdapter {
private static final String KEY_FRAGMENTS = "fragments";
private static final String KEY_STATES = "states";
private final FragmentManager fragmentManager;
private final LongSparseArray<Fragment> fragments = new LongSparseArray<>();
private final LongSparseArray<Fragment.SavedState> savedStates = new LongSparseArray<>();
private FragmentTransaction currentTransaction;
private Fragment currentPrimaryItem;
public DynamicFragmentStatePagerAdapter(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
public abstract Fragment getItem(int position);
public abstract long getItemId(int position);
@Nullable
public Fragment getItemById(long itemId) {
return fragments.get(itemId);
}
@Override
@SuppressLint("CommitTransaction")
public Object instantiateItem(ViewGroup container, int position) {
final long itemId = getItemId(position);
Fragment fragment = fragments.get(itemId);
if (fragment != null) {
return fragment;
}
fragment = getItem(position);
final Fragment.SavedState savedState = savedStates.get(itemId);
if (savedState != null) {
fragment.setInitialSavedState(savedState);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
fragments.put(itemId, fragment);
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.add(container.getId(), fragment);
return fragment;
}
@Override
@SuppressLint("CommitTransaction")
public void destroyItem(ViewGroup container, int position, Object object) {
final Fragment fragment = (Fragment) object;
final int index = fragments.indexOfValue(fragment);
final long itemId = fragments.keyAt(index);
if (fragment.isAdded() && getItemPosition(fragment) != POSITION_NONE) {
savedStates.put(itemId, fragmentManager.saveFragmentInstanceState(fragment));
} else {
savedStates.remove(itemId);
}
fragments.removeAt(index);
if (currentTransaction == null) {
currentTransaction = fragmentManager.beginTransaction();
}
currentTransaction.remove(fragment);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
final Fragment fragment = (Fragment) object;
if (currentPrimaryItem != fragment) {
if (currentPrimaryItem != null) {
currentPrimaryItem.setMenuVisibility(false);
currentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
currentPrimaryItem = fragment;
}
}
@Override
public void startUpdate(ViewGroup container) {
if (container.getId() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this + " requires a view id");
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (currentTransaction != null) {
currentTransaction.commitNowAllowingStateLoss();
currentTransaction = null;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment) object).getView() == view;
}
@Override
public int getItemPosition(Object object) {
final Fragment fragment = (Fragment) object;
final int index = fragments.indexOfValue(fragment);
if (index != -1) {
final long itemId = fragments.keyAt(index);
for (int position = 0, itemCount = getCount(); position < itemCount; position++) {
final long currentItemId = getItemId(position);
if (currentItemId == itemId) {
return position;
}
}
}
return POSITION_NONE;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
purgeStates();
}
@Override
public Parcelable saveState() {
final Bundle state = new Bundle();
final int stateCount = savedStates.size();
if (stateCount > 0) {
final Bundle stateBundle = new Bundle();
for (int i = 0; i < stateCount; i++) {
stateBundle.putParcelable(String.valueOf(savedStates.keyAt(i)), savedStates.valueAt(i));
}
state.putBundle(KEY_STATES, stateBundle);
}
final int fragmentCount = fragments.size();
if (fragmentCount > 0) {
final Bundle fragmentBundle = new Bundle();
for (int i = 0; i < fragmentCount; i++) {
final Fragment fragment = fragments.valueAt(i);
if (fragment.isAdded()) {
fragmentManager.putFragment(fragmentBundle, String.valueOf(fragments.keyAt(i)), fragment);
}
}
state.putBundle(KEY_FRAGMENTS, fragmentBundle);
}
return state;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
final Bundle bundle = (Bundle) state;
bundle.setClassLoader(loader);
fragments.clear();
savedStates.clear();
final Bundle stateBundle = bundle.getBundle(KEY_STATES);
if (stateBundle != null) {
for (String key : stateBundle.keySet()) {
savedStates.put(Long.parseLong(key), stateBundle.getParcelable(key));
}
}
final Bundle fragmentsBundle = bundle.getBundle(KEY_FRAGMENTS);
if (fragmentsBundle != null) {
for (String key : fragmentsBundle.keySet()) {
final Fragment fragment = fragmentManager.getFragment(fragmentsBundle, key);
if (fragment != null) {
fragment.setMenuVisibility(false);
fragments.put(Long.parseLong(key), fragment);
}
}
}
}
private void purgeStates() {
final Set<Long> itemIds = new HashSet<>();
for (int position = 0, itemCount = getCount(); position < itemCount; position++) {
final long itemId = getItemId(position);
itemIds.add(itemId);
}
for (int index = 0; index < savedStates.size(); ) {
final long itemId = savedStates.keyAt(index);
if (!itemIds.contains(itemId)) {
savedStates.removeAt(index);
} else {
index++;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment