避免在执行制表符切换时重新创build相同的视图

目前,我有2个Fragments ,这是可以通过ActionBar的标签切换。

  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); } } 

我意识到我的Activity (这个活动持有2个碎片)第一次启动, Fragment的方法将按以下顺序调用。

onCreate -> onCreateView -> onStart

当我执行Tab切换,然后Tab切换回相同的片段,下面的方法将被再次调用。

onCreateView -> onStart

当Tab切换回来时,我只想保留相同的GUI视图状态。

  • 我希望我的图表继续放大到以前的水平。
  • 我希望我的图表水平滚动保持在以前的水平。
  • 我想我的列表继续滚动停留在上一级。

我知道我可以保存/恢复简单的variables使用下面的方法切换标签

Android的片段 – 如何保存片段中的视图的状态,当另一个片段被推上它的顶部

但是,这不是我想要的,因为我的GUI状态在整个原始值中很难描述。

我尝试以下方法。 当然,它不会工作,因为我得到以下运行时错误。

 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:指定的子项已经有父项。 您必须先调用子对象的父对象的removeView()。

我意识到下面的演示例子能够在Tab切换时保存其片段GUI状态(例如列表的垂直滚动位置)。 但我想,也许是因为他们使用ListFragment? 因为我没有发现他们执行任何特殊处理来保存GUI状态。

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

我可以知道,如何在执行制表符切换时避免重新创build相同的视图?

Solutions Collecting From Web of "避免在执行制表符切换时重新创build相同的视图"

我有同样的问题,并试图按照错误消息中的build议。 我试了下面的代码,它为我工作。

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) { if (mMyView == null) { mMyView = new MyView(getActivity()); } else { ((ViewGroup) mMyView.getParent()).removeView(mMyView); } return mPuzzleView; } 

我在几个小时前开始寻找一个简单的解决scheme,最后偶然发现@roger的答案,这为我节省了很多头发。

在其他实现中使用ViewPager时,我可以简单地调用:

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

所以,我非常惊讶,花了我这么多小时来解决这个问题。 他给出的例子并不完全清楚,所以为了完整起见,这里是我用于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; } } 

我search了下面我要添加的关键短语,希望我可以从别人的挫折中拯救别人。


FragmentTabHost保存Fragment状态

FragmentTabHost视图重新创build

FragmentTabHostcaching碎片

FragmentTabHost onCreateView片段被破坏


以下解决scheme适用于我。 它可以防止在切换选项卡时调用片段的onCreateView。

Activity的onCreate应该添加所有的片段,并隐藏除第一个选项卡之外的所有片段:

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

活动的onTabSelected应该只隐藏当前片段并显示与所选标签对应的片段。

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

请注意,更改设备方向将重新启动您的活动,然后重新创build您的碎片。 你可以通过在你的Manifest中添加这个configChanges来避免这种情况:

 <activity android:configChanges="keyboardHidden|orientation" ... 
 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; } 

更新

我只是通过使用ViewPager而不是ActionBar的选项卡来避免这个问题。

我面临同样的问题,但我所做的是,在附加或分离ActionBar.TabListener的callback内的分割之前,调用

 fragmentManager.executePendingTransactions(); 

这为我解决了这个问题

 @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); } }