My activity uses TabLayout
+ ViewPager
.
The number of tabs and pages here are dynamic depending on the data fetch from the server.
The crash are reported via Crashlytics, I'm not able to replicate it.
My Activity code:
@Override
protected void onCreate(Bundle savedInstanceState) {
boolean isAppRestarting = PrefUtils.getBoolean("app_restarting", false);
if (isAppRestarting) {
super.onCreate(null);
this.savedInstanceState = null;
} else {
super.onCreate(savedInstanceState);
this.savedInstanceState = savedInstanceState;
}
initSlidingTabs();
fetchSomeData(); // see onDataFetched() below
}
private void initSlidingTabs() {
List<FragmentWithTitle> sdsTabs = new ArrayList<>();
Logger.i("There is saved instance state");
ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES);
if (titles != null) {
for (int i = 0; i < titles.size(); i++) {
if (getFragment(i) != null) {
sdsTabs.add(new FragmentWithTitle(getFragment(i), titles.get(i)));
}
}
}
mTabsAdapter = new MyTabsAdapter(getSupportFragmentManager(), sdsTabs);
mViewPager = (ViewPager) findViewById(R.id.layoutViewPagerSlidingTabs);
mViewPager.setAdapter(mTabsAdapter);
mViewPager.setOffscreenPageLimit(2);
mSlidingTabs = (TabLayout) findViewById(R.id.tabs);
mSlidingTabs.setupWithViewPager(mViewPager);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mTabsAdapter != null) {
outState.putStringArrayList(TAB_TITLES, mTabsAdapter.getTitles());
}
}
private Fragment getFragment(int position) {
return savedInstanceState == null ? mTabsAdapter.getItem(position) : getSupportFragmentManager()
.findFragmentByTag(getFragmentTag(position));
}
private String getFragmentTag(int position) {
return "android:switcher:" + R.id.layoutViewPagerSlidingTabs + ":" + position;
}
/**
Callback of fetchData() in onCreate()
*/
private void onDataFetched(List<AbcObject> abcObjects) {
if (savedInstanceState == null) {
if (abcObjects != null) {
for (AbcObject abcObject : abcObjects) {
mTabsAdapter.addTab(new FragmentWithTitle(AbcFragment.newInstance(), abcObject.getTitle()));
}
}
} else {
Logger.d("There is saved instance state");
ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES);
if (titles != null) {
for (int i = 0; i < titles.size(); i++) {
mTabsAdapter.addTab(new FragmentWithTitle(getFragment(i), titles.get(i)));
}
}
}
mTabsAdapter.notifyDataSetChanged();
}
MyTabsAdapter:
public class MyTabsAdapter extends FragmentPagerAdapter {
private List<FragmentWithTitle> mTabs = new ArrayList<>();
public MyTabsAdapter(FragmentManager fm, List<FragmentWithTitle> sdsTabs) {
super(fm);
this.mTabs = sdsTabs;
}
@Override
public Fragment getItem(int position) {
return mTabs.get(position).getFragment();
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).getTitle();
}
@Override
public int getCount() {
return this.mTabs.size();
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
/**
* Prevent java.lang.IllegalStateException Fragment no longer exists for key android:target_state: index 5
*
* @return
*/
@Override
public Parcelable saveState() {
return null;
}
public ArrayList<String> getTitles() {
ArrayList<String> titles = new ArrayList<>();
for (int i = 0; i < mTabs.size(); i++) {
titles.add((String) mTabs.get(i).getTitle());
}
return titles;
}
public void addTab(FragmentWithTitle fragmentWithTitle) {
mTabs.add(fragmentWithTitle);
}
}
Full stacktrace:
Fatal Exception: java.lang.IllegalStateException: Can't change tag of fragment ShopFrag{4dbb6e90 #0 id=0x7f1200dd android:switcher:2131886301:0}: was android:switcher:2131886301:0 now android:switcher:2131886301:9
at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:444)
at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:426)
at android.support.v4.app.FragmentPagerAdapter.instantiateItem(FragmentPagerAdapter.java:103)
at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1038)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1252)
at android.support.v4.view.ViewPager.populate(ViewPager.java:1120)
at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1646)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
at android.view.View.measure(View.java:17396)
at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1081)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:695)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:588)
at android.view.View.measure(View.java:17396)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2505)
at android.view.View.measure(View.java:17396)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2175)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1316)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1513)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1200)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6401)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803)
at android.view.Choreographer.doCallbacks(Choreographer.java:603)
at android.view.Choreographer.doFrame(Choreographer.java:573)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5335)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(NativeStart.java)
FragmentWithTitle:
public class FragmentWithTitle {
public FragmentWithTitle(Fragment mFragment, CharSequence mTitle) {
this.fragment = mFragment;
this.title = mTitle;
}
public Fragment getFragment() {
return fragment;
}
public void setFragment(Fragment fragment) {
this.fragment = fragment;
}
public CharSequence getTitle() {
return title;
}
public void setTitle(CharSequence title) {
this.title = title;
}
private Fragment fragment;
private CharSequence title;
}
The FragmentPagerAdapter
already caches the Fragments
for you. Each fragment is assigned a tag, and then the FragmentPagerAdapter
tries to call findFragmentByTag
. It only calls getItem() if the result from findFragmentByTag
is null
.
You're probably getting this error because you're adding the same fragment instance to the list. You should create a new instance for each page.
Example form document :
//...
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);// IMPORTANT
}
}
//..
Refer :FragmentPagerAdapter
Main thread : Retrieve a Fragment from a ViewPager