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, Fragment
s’ 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
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?
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;
}
Answer:
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
Answer:
The following solution works for me. It prevents Fragment’s onCreateView to be called when switching tabs.
Activity’s onCreate should add all fragments and hide all except the 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;
Activity’s onTabSelected should just hide the current fragment and show the fragment corresponding to the chosen tab.
ft.hide(currentFragment);
ft.show(chosenFragment);
ft.commit();
currentFragment = chosenFragment;
Beware that changing the device orientation will restart your Activity and then recreate your Fragments. You can avoid that by adding this configChanges in your Manifest:
<activity android:configChanges="keyboardHidden|orientation" ...
Answer:
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;
}
Answer:
Update
I simply avoiding this problem, by using ViewPager
instead of ActionBar
‘s tab.
Answer:
I faced the same issue, but what I did was, before attaching or detaching the fragement inside the callbacks of ActionBar.TabListener, call
fragmentManager.executePendingTransactions();
this solves the issue 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);
}
}
Tags: androidandroid, view