Created
August 2, 2015 17:08
-
-
Save carlonzo/a6fc852b6167ae6c30ee to your computer and use it in GitHub Desktop.
FragmentStatePagerAdapter that caches each pages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2014 Soichiro Kashima | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.github.ksoichiro.android.observablescrollview; | |
import android.os.Bundle; | |
import android.os.Parcelable; | |
import android.support.v4.app.Fragment; | |
import android.support.v4.app.FragmentManager; | |
import android.support.v4.app.FragmentStatePagerAdapter; | |
import android.util.SparseArray; | |
import android.view.ViewGroup; | |
/** | |
* FragmentStatePagerAdapter that caches each pages. | |
* FragmentStatePagerAdapter is also originally caches pages, | |
* but its keys are not public nor documented, so depending | |
* on how it create cache key is dangerous. | |
* This adapter caches pages by itself and provide getter method to the cache. | |
*/ | |
public abstract class CacheFragmentStatePagerAdapter extends FragmentStatePagerAdapter { | |
private static final String STATE_SUPER_STATE = "superState"; | |
private static final String STATE_PAGES = "pages"; | |
private static final String STATE_PAGE_INDEX_PREFIX = "pageIndex:"; | |
private static final String STATE_PAGE_KEY_PREFIX = "page:"; | |
private FragmentManager mFm; | |
private SparseArray<Fragment> mPages; | |
public CacheFragmentStatePagerAdapter(FragmentManager fm) { | |
super(fm); | |
mPages = new SparseArray<Fragment>(); | |
mFm = fm; | |
} | |
@Override | |
public Parcelable saveState() { | |
Parcelable p = super.saveState(); | |
Bundle bundle = new Bundle(); | |
bundle.putParcelable(STATE_SUPER_STATE, p); | |
bundle.putInt(STATE_PAGES, mPages.size()); | |
if (0 < mPages.size()) { | |
for (int i = 0; i < mPages.size(); i++) { | |
int position = mPages.keyAt(i); | |
bundle.putInt(createCacheIndex(i), position); | |
Fragment f = mPages.get(position); | |
mFm.putFragment(bundle, createCacheKey(position), f); | |
} | |
} | |
return bundle; | |
} | |
@Override | |
public void restoreState(Parcelable state, ClassLoader loader) { | |
Bundle bundle = (Bundle) state; | |
int pages = bundle.getInt(STATE_PAGES); | |
if (0 < pages) { | |
for (int i = 0; i < pages; i++) { | |
int position = bundle.getInt(createCacheIndex(i)); | |
Fragment f = mFm.getFragment(bundle, createCacheKey(position)); | |
mPages.put(position, f); | |
} | |
} | |
Parcelable p = bundle.getParcelable(STATE_SUPER_STATE); | |
super.restoreState(p, loader); | |
} | |
/** | |
* Get a new Fragment instance. | |
* Each fragments are automatically cached in this method, | |
* so you don't have to do it by yourself. | |
* If you want to implement instantiation of Fragments, | |
* you should override {@link #createItem(int)} instead. | |
* | |
* {@inheritDoc} | |
* | |
* @param position position of the item in the adapter | |
* @return fragment instance | |
*/ | |
@Override | |
public Fragment getItem(int position) { | |
Fragment f = createItem(position); | |
// We should cache fragments manually to access to them later | |
mPages.put(position, f); | |
return f; | |
} | |
@Override | |
public void destroyItem(ViewGroup container, int position, Object object) { | |
if (0 <= mPages.indexOfKey(position)) { | |
mPages.remove(position); | |
} | |
super.destroyItem(container, position, object); | |
} | |
/** | |
* Get the item at the specified position in the adapter. | |
* | |
* @param position position of the item in the adapter | |
* @return fragment instance | |
*/ | |
public Fragment getItemAt(int position) { | |
return mPages.get(position); | |
} | |
/** | |
* Create a new Fragment instance. | |
* This is called inside {@link #getItem(int)}. | |
* | |
* @param position position of the item in the adapter | |
* @return fragment instance | |
*/ | |
protected abstract Fragment createItem(int position); | |
/** | |
* Create an index string for caching Fragment pages. | |
* | |
* @param index index of the item in the adapter | |
* @return key string for caching Fragment pages | |
*/ | |
protected String createCacheIndex(int index) { | |
return STATE_PAGE_INDEX_PREFIX + index; | |
} | |
/** | |
* Create a key string for caching Fragment pages. | |
* | |
* @param position position of the item in the adapter | |
* @return key string for caching Fragment pages | |
*/ | |
protected String createCacheKey(int position) { | |
return STATE_PAGE_KEY_PREFIX + position; | |
} | |
} |
The error occurs:
java.lang.IllegalStateException: Fragment PlacesMapFragment{e264ad0 (36b68aeb-efd5-4d60-8dd1-65368a752a90)} is not currently in the FragmentManager
at androidx.fragment.app.FragmentManagerImpl.putFragment(FragmentManagerImpl.java:350)
at com.example.CacheFragmentStatePagerAdapter.saveState(CacheFragmentStatePagerAdapter.java:47)
at androidx.viewpager.widget.ViewPager.onSaveInstanceState(ViewPager.java:1445)
at android.view.View.dispatchSaveInstanceState(View.java:18792)
at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:3905)
at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:3911)
at android.view.View.saveHierarchyState(View.java:18775)
at androidx.fragment.app.FragmentManagerImpl.saveFragmentViewState(FragmentManagerImpl.java:2263)
at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:944)
at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1228)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:434)
at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2066)
at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1856)
at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1811)
at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1717)
at androidx.fragment.app.FragmentManagerImpl$2.run(FragmentManagerImpl.java:150)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this implementation is really good and useful - but slight modification in 72 on restoreinstance where the old mPage resides in memory seems and contains old value.clear the mPage(mPage.clear() ) and start setting the fragment by getFragment()
this solved issue when i changed orientation between portrait and landscape.