【问题标题】:Avoid recreating same view when perform tab switching执行选项卡切换时避免重新创建相同的视图
【发布时间】:2012-05-23 08:51:43
【问题描述】:

当前,我有 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个片段)第一次启动时,Fragments'的方法将按以下顺序调用。

onCreate -> onCreateView -> onStart

当我执行 Tab 切换,然后 Tab 切换回同一个 Fragment 时,会再次调用以下方法。

onCreateView -> onStart

我只是希望在切换回 Tab 时保持相同的 GUI 视图状态。

  • 我希望我的图表继续放大到之前的级别。
  • 我希望我的图表水平滚动保持在上一个级别。
  • 我希望我的列表继续滚动保持在上一级。
  • ...

我知道在Tab切换时可以使用以下方法保存/恢复简单变量

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

但是,这不是我想要的,因为我的 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

请问,如何避免在切换标签页时重新创建相同的视图?

【问题讨论】:

  • 你知道怎么做吗?
  • 遇到同样的问题。特别是因为我的一个选项卡具有动态添加的字段。
  • 这里也一样。你会认为这很简单......

标签: android


【解决方案1】:

我遇到了同样的问题,并尝试按照错误消息中的建议进行操作。 我尝试了以下代码,它对我有用。

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

     return mPuzzleView; 
}

【讨论】:

  • 天啊,在搜索了这么多并花了几个小时之后,这节省了我的一天:D...thnx 很多
  • 能否更新完整代码sn-p;我没有什么疑问;
【解决方案2】:

几个小时前我开始寻找一个简单的解决方案,最后偶然发现了the answer by @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;
    }
}

我搜索了以下我在这里添加的关键短语,希望我可以拯救其他人免受我刚刚经历的挫败感!


FragmentTabHost 保存 Fragment 状态

FragmentTabHost 视图重新创建

FragmentTabHost 缓存片段

FragmentTabHost onCreateView 片段被破坏


【讨论】:

  • 在 ((ViewGroup) rootView.getParent()).removeView(rootView); 获取 npe,我正在尝试的代码是 this
  • 有史以来最好的答案......你拯救了我的一天:)
  • @nawaabsaab 你修好了吗?我这里有同样的错误。
【解决方案3】:

以下解决方案对我有用。它可以防止在切换选项卡时调用 Fragment 的 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;

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

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

请注意,更改设备方向会重新启动您的 Activity,然后重新创建您的 Fragment。您可以通过在清单中添加此 configChanges 来避免这种情况:

<activity android:configChanges="keyboardHidden|orientation" ...

【讨论】:

  • 但是@Olivier C... 我无法使用 getSupportFragmentManager() 方法,因为我正在实现 ActionBar SHerlock...。请问我该怎么办?
【解决方案4】:
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; 
    }

【讨论】:

    【解决方案5】:

    更新

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

    【讨论】:

      【解决方案6】:

      我遇到了同样的问题,但我所做的是,在 ActionBar.TabListener 的回调中附加或分离片段之前,调用

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多