首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何在添加到后台堆栈时维护片段状态?

如何在添加到后台堆栈时维护片段状态?
EN

Stack Overflow用户
提问于 2012-07-06 05:50:40
回答 15查看 157.7K关注 0票数 168

我已经编写了一个在两个片段之间切换的虚拟活动。当您从FragmentA转到FragmentB时,FragmentA将被添加到后端堆栈。然而,当我返回到FragmentA (通过按back)时,一个全新的FragmentA被创建了,它所处的状态也丢失了。我感觉我在追求和this问题一样的东西,但我已经包含了一个完整的代码样本来帮助根治这个问题:

代码语言:javascript
复制
public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

添加了一些日志消息:

代码语言:javascript
复制
07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

它永远不会调用FragmentA.onSaveInstanceState,当你回击时,它会创建一个新的FragmentA。但是,如果我在FragmentA上锁定了屏幕,就会调用FragmentA.onSaveInstanceState。所以weird...am我错误地期望添加到后台堆栈的片段不需要重新创建?以下是docs的说法:

然而,如果在删除片段时调用addToBackStack(),则该片段将停止,并在用户返回时恢复。

EN

回答 15

Stack Overflow用户

回答已采纳

发布于 2012-07-06 06:38:21

如果您从后台堆栈返回到某个片段,则它不会重新创建该片段,而是重用相同的实例,并在片段生命周期中以onCreateView()开头,请参见Fragment lifecycle

因此,如果您想要存储状态,则应该使用实例变量和,而不是,而要依赖onSaveInstanceState()

票数 124
EN

Stack Overflow用户

发布于 2014-05-08 13:40:41

与苹果的UINavigationControllerUIViewController相比,谷歌在安卓软件架构方面做得并不好。而安卓关于Fragment的文档也没有多大帮助。

从FragmentA进入FragmentB时,不会销毁已有的FragmentA实例。当您在FragmentB中按Back并返回到FragmentA时,我们不会创建新的FragmentA实例。将调用现有FragmentA实例的onCreateView()

关键是我们不应该在FragmentA的 onCreateView()**,中再次膨胀视图,因为我们正在使用现有的FragmentA的实例。我们需要保存并重用rootView。**

下面的代码运行良好。它不仅保持碎片状态,而且还减少了RAM和CPU负载(因为我们只在必要时膨胀布局)。我不敢相信谷歌的示例代码和文档只提到了always inflate layout

版本1 (不使用版本1,使用版本2)

代码语言:javascript
复制
public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

-更新于2005年5月3日:

正如注释中提到的,有时onCreateView中的_rootView.getParent()为null,这会导致崩溃。按照dell116的建议,版本2删除了onDestroyView()中的_rootView。已在Android 4.0.3、4.4.4、5.1.0上测试。

版本2

代码语言:javascript
复制
public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

警告!

这是个黑客!虽然我正在我的应用程序中使用它,但您需要仔细测试和阅读评论。

票数 81
EN

Stack Overflow用户

发布于 2013-09-26 16:00:49

我想有一种替代方法可以实现你想要的东西。我不是说这是一个完整的解决方案,但它在我的案例中起到了作用。

我所做的不是替换片段,而是添加了目标片段。因此,基本上您将使用add()方法来代替replace()

我还做了什么。我隐藏了当前的片段,并将其添加到backstack中。

因此,它将新片段覆盖在当前片段之上,而不会破坏它的视图。(检查它的onDestroyView()方法没有被调用。另外,将它添加到backstate中可以让我恢复片段。

代码如下:

代码语言:javascript
复制
Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

AFAIK系统仅在视图被销毁或未创建时才调用onCreateView()。但在这里,我们通过不从内存中删除视图来保存它。因此,它不会创建新视图。

当你从目标片段返回时,它将弹出最后一个移除顶部片段的FragmentTransaction,这将使最顶部(SourceFragment的)视图出现在屏幕上。

评论:正如我所说的,这不是一个完整的解决方案,因为它没有移除Source fragment的视图,因此占用了比平时更多的内存。但不管怎样,还是要达到目的。此外,我们使用了一种完全不同的隐藏视图的机制,而不是替换非传统的视图。

因此,这并不是关于如何维护状态,而是关于如何维护视图。

票数 55
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11353075

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档