Avoid recreating the same view when switching tabs

Current, I have 2 Fragments that can switch through the ActionBar tab.

  getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); ActionBar.Tab newTab = getSupportActionBar().newTab(); newTab.setText("history"); newTab.setTabListener(new TabListenerHistoryFragment>(this, "history", HistoryFragment.class)); 



  @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // Check if the fragment is already initialized if (mFragment == null) { // If not, instantiate and add it to the activity mFragment = Fragment.instantiate(mActivity, mClass.getName()); mFragment.setRetainInstance(true); ft.add(android.R.id.content, mFragment, mTag); } else { // If it exists, simply attach it in order to show it ft.attach(mFragment); } } 

I understand that the first time my Activity (this activity contains 2 fragments) starts, Fragment s' methods will be called in the following sequence.

onCreate -> onCreateView -> onStart

When I switch Tab and then Tab switch back to the same fragment, the following methods will be called again.

onCreateView -> onStart

I just want to keep the GUI view state the same when Tab switches again.

  • I want my schedule to continue to grow to the previous level.
  • I want my horizontal scroll bar to stay the same.
  • I want my list to continue scrolling to the previous level.
  • ...

I know that I can save / restore simple variables using the following method when switching Tab

Android fragment. How to save view states in a fragment when another fragment is clicked on top of it.

But this is not what I want, since my GUI state is quite difficult to describe in general a bunch of primitive values.

I am trying the following approach. Of course, this will not work, as I get the following runtime error.

 public class HistoryFragment extends Fragment { View view = null; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (this.view != null) { return this.view; } this.view = inflater.inflate(R.layout.history_activity, container, false); } } 

java.lang.IllegalStateException: The specified child already has a parent. You must first call removeView () on the parent parent.

I understand that the following demo can save the state of its GUI fragment (for example, the position of the vertical scroll of the list) when switching Tab. But I think maybe it's because they use ListFragment? Since I do not believe that they perform any special processing to preserve the state of the GUI.

  • com.example.android.apis.app.FragmentTabs
  • com.example.android.apis.app.LoaderCursor.CursorLoaderListFragment

Can I find out how I can avoid re-creating the same view when switching to a tab?

+30
android
May 23 '12 at 8:51
source share
6 answers

I had the same problem and tried to execute the sentence in the error message. I tried the following code and it worked for me.

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) { if (mMyView == null) { mMyView = new MyView(getActivity()); } else { ((ViewGroup) mMyView.getParent()).removeView(mMyView); } return mPuzzleView; } 
+36
Aug 11 2018-12-12T00:
source share

I started looking for a simple solution for this many hours ago and finally came across an answer from @roger that saved me a lot of hair ....

When using ViewPager in other implementations, I can simply call:

 mViewPager.setOffscreenPageLimit(//number of pages to cache); 

So, I was very surprised that it took me so many hours to solve this. The example he gave was not entirely clear, so for completeness, here is the code I use for fragments in FragmentTabHost

 import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FragmentExample extends Fragment { private View rootView; public FragmentExample() { } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { if (rootView == null) { rootView = inflater.inflate(R.layout.fragment_example_layout, container, false); // Initialise your layout here } else { ((ViewGroup) rootView.getParent()).removeView(rootView); } return rootView; } } 

I searched for the following key phrases that I am adding here, in the hope that I can save someone from the disappointment I just experienced!




FragmentTabHost save fragment state

Playing FragmentTabHost Fragments

Fragments of a fragment of a fragment

FragmentTabHost onCreateView Fragment destroyed




+17
Sep 12 '14 at 16:13
source share

The following solution works for me. This prevents calling Fragment onCreateView when switching tabs.

The onCreate action should add all the fragments and hide all but one for the first tab:

 ft.add(R.id.fragment_content, secondTabFragment); ft.hide(secondTabFragment); ft.add(R.id.fragment_content, firstTabFragment); ft.show(firstTabFragment); ft.commit(); currentFragment = firstTabFragment; 

The onTabSelected activity should simply hide the current fragment and show the fragment corresponding to the selected tab.

 ft.hide(currentFragment); ft.show(chosenFragment); ft.commit(); currentFragment = chosenFragment; 

Beware that changing the orientation of the device will restart your action and then restore fragments. You can avoid this by adding this configChanges to your manifest:

 <activity android:configChanges="keyboardHidden|orientation" ... 
+6
Oct 18
source share
 View mMyView = null; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) { if (state == null) { mMyView = new MyView(getActivity()); } else { container.removeView(mMyView); } return mMyView; } 
+3
Jun 02 '15 at 23:15
source share

Update

I just avoid this problem by using ViewPager instead of the ActionBar tab.

0
May 27 '15 at 15:28
source share

I ran into the same problem, but I did this before attaching or detaching the fragmentation inside the ActionBar.TabListener callbacks, calling

 fragmentManager.executePendingTransactions(); 

this solves the problem for me

 @Override public void onTabelected(Tab tab, FragmentTransaction ft, FragmentManager fm) { fm.executePendingTransactions(); // **execute the pending transactions before adding another fragment. if (mFragment == null) { mFragment = Fragment.instantiate(mContext, mFragmentName); ft.replace(android.R.id.tabcontent, mFragment, mTag); } else { ft.attach(mFragment); } } 
-one
Mar 31 '14 at 11:35
source share



All Articles