为了账号安全,请及时绑定邮箱和手机立即绑定

添加到后台堆栈后如何保持片段状态?

添加到后台堆栈后如何保持片段状态?

开心每一天1111 2019-11-05 16:13:24
我写了一个虚拟活动,可以在两个片段之间切换。当您从FragmentA转到FragmentB时,FragmentA被添加到后台堆栈中。但是,当我返回FragmentA(按回去)时,会创建一个全新的FragmentA,并且它所处的状态会丢失。我感觉自己对这个问题也很满意,但是我提供了完整的代码示例来帮助解决问题: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);    }  }}它从不调用FragmentA.onSaveInstanceState,并且在您回击时会创建一个新的FragmentA。但是,如果我在FragmentA上并且锁定了屏幕,则会调用FragmentA.onSaveInstanceState。所以很奇怪...我期望添加到后端堆栈的片段不需要重新创建是我错了吗?这是文档所说的:而如果您在删除片段时确实调用了addToBackStack(),则该片段将停止并在用户向后导航时恢复。
查看完整描述

3 回答

?
扬帆大鱼

TA贡献1799条经验 获得超9个赞

如果从后堆栈返回片段,它不会重新创建片段,而是会重新使用同一实例,并从onCreateView()片段生命周期开始,请参见片段生命周期。


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


查看完整回答
反对 回复 2019-11-05
?
Helenr

TA贡献1780条经验 获得超4个赞

与Apple UINavigationController和相比UIViewController,Google在Android软件架构方面做得不好。与Android有关的文档Fragment并没有太大帮助。


当您从FragmentA输入FragmentB时,现有的FragmentA实例不会被破坏。当您在FragmentB中按Back返回到FragmentA时,我们不会创建新的FragmentA实例。现有的FragmentA实例onCreateView()将被调用。


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


以下代码运行良好。它不仅保持片段状态,还减少了RAM和CPU负载(因为我们仅在必要时膨胀布局)。我不能相信谷歌的示例代码和文档别说但总是夸大布局。


版本1(请勿使用版本1。请使用版本2)


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;

    }

}


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


版本2


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

    }

}

警告!!!


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


查看完整回答
反对 回复 2019-11-05
?
慕村9548890

TA贡献1884条经验 获得超4个赞

我猜想有另一种方法可以实现您想要的。我没有说这是一个完整的解决方案,但在我的情况下达到了目的。


我所做的是代替了我刚刚添加目标片段的片段。因此,基本上,您将改为使用add()method replace()。


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


因此,它在不破坏其视图的情况下将新片段覆盖在当前片段上。(检查onDestroyView()是否未调用其方法。加上它可以backstate使我恢复片段。


这是代码:


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()在视图被破坏或未创建时调用。但是这里我们通过不从内存中删除视图来保存视图。因此,它不会创建新视图。


当您从Destination Fragment返回时,它将弹出最后一个FragmentTransaction删除的顶部片段,这将使最上面的(SourceFragment's)视图显示在屏幕上。


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


因此,这实际上与维护状态无关,而与维护视图有关。


查看完整回答
反对 回复 2019-11-05
  • 3 回答
  • 0 关注
  • 477 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信