Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid recreating same view when perform tab switching

Tags:

android

Current, I have 2 Fragments, which is switch-able through ActionBar's 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 realize the first time of my Activity (This activity is holding 2 fragments) being launched, Fragments' methods will be called in the following sequence.

onCreate -> onCreateView -> onStart

When I perform Tab switching, and then Tab switching back to the same Fragment, the following methods will be called again.

onCreateView -> onStart

I just wish to retain the same GUI view state, when Tab is being switched back.

  • I want my chart continue to be zoomed into previous level.
  • I want my chart horizontal scroll stay at previous level.
  • I want my list continue scroll stay at previous level.
  • ...

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

android fragment- How to save states of views in a fragment when another fragment is pushed on top of it

But, that is not something I want, as my GUI state is pretty difficult to describe within whole bunch of primitive values.

I try the following approach. Of course it won't work, as I am getting 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 call removeView() on the child's parent first.

I realize the following demo example is able to preserve its fragment GUI state (For instance, the position of vertical scroll of list) when there is Tab switching. But I guess, perhaps it is because they are using ListFragment? As I do not find they perform any special handling to preserve GUI state.

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

May I know, how I can avoid from recreating same view when perform tab switching?

like image 852
Cheok Yan Cheng Avatar asked May 23 '12 08:05

Cheok Yan Cheng


People also ask

What is FragmentManager in Android?

FragmentManager is the class responsible for performing actions on your app's fragments, such as adding, removing, or replacing them, and adding them to the back stack.

What is backstack in Fragment?

Android OS provides a back stack function for Activity, it also provides the back stack function for Fragment. If you add one Fragment into the back stack, when you press the android device back menu, you can find the Fragment that is saved in the back stack popup.

When onviewcreated is called?

oncreate view instantiates the view, onviewcreated is called after oncreateview and before saved states are restored... it's more a timing issue in the lifecycle of the fragment. – me_ Oct 23, 2018 at 5:57.


2 Answers

I had the same problem, and tried to follow the suggestion 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;  } 
like image 146
roger Avatar answered Oct 14 '22 14:10

roger


I started searching for a simple solution for this many hours ago and finally stumbled across the answer by @roger which saved me lots of hair....

When using the ViewPager in other implementations, I could simply call:

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

So, I was very surprised it took me so many hours to resolve this. The example he gave wasn't entirely clear though, so for the sake of completeness, here is the code I use for the Fragments in my 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 which I'm adding here, in the hope that I may save someone else from the frustration I've just experienced!


FragmentTabHost save Fragment state

FragmentTabHost views recreated

FragmentTabHost cache Fragments

FragmentTabHost onCreateView Fragment destroyed


like image 29
brandall Avatar answered Oct 14 '22 13:10

brandall