add()、replace()和addToBackStack()之间的区别

362

这些方法之间的主要区别是什么:

fragmentTransaction.addToBackStack(name);
fragmentTransaction.replace(containerViewId, fragment, tag);
fragmentTransaction.add(containerViewId, fragment, tag);

替换已经存在的片段、将片段添加到活动状态中、将活动添加到后退栈意味着什么?

其次,findFragmentByTag()方法会搜索由add()/replace()方法或addToBackStack()方法添加的标签吗?

9个回答

398

addreplace 之间的一个更重要的区别是:

replace 移除现有片段并添加一个新片段。这意味着当您按下后退按钮时,被替换的片段将被创建,并调用其 onCreateView 方法。而 add 会保留现有的片段并添加一个新片段,这意味着现有的片段仍然处于活动状态,因此在按下后退按钮时,现有片段的 onCreateView 不会被调用(即,在添加新片段之前存在的片段)。

就片段的生命周期事件而言,replace 会调用 onPauseonResumeonCreateView 和其他生命周期事件,但 add 不会调用它们。

编辑: 如果使用像 Greenrobot 的 Eventbus 这样的事件总线库,并通过 add 重复使用同一片段来堆叠片段,则需要小心。在这种情况下,即使您遵循最佳实践,在 onResume 中注册事件总线并在 onPause 中注销,每个添加的片段实例中的事件总线仍将处于活动状态。因此,每个处于活动状态的片段实例中的事件总线监听器都将处理相同的事件,这可能不是您想要的结果。


1
我认为一种方法是在最顶层的片段中处理事件,并在处理完成后调用cancelEventDelivery()。您可以在此处找到有关cancelEventDelivery()方法的更多信息 https://github.com/greenrobot/EventBus/blob/master/HOWTO.md#cancelling-event-delivery - Jeevan
8
请注意,用新片段替换当前片段意味着在从片段堆栈中弹出时需要重新创建以获取先前的片段。+1 支持您。 - AndaluZ
onPause,onResume与宿主Activity紧密相关。当替换片段时,它们不会被调用。 - Zar E Ahmer
只是补充一下,如果您正在使用EventBus,可以使用标签添加片段,并从片段传递该标签到事件中,然后无论如何都会检查所有的eventbus,您只需指定应执行哪个。 - user2582318
你必须在调用add()或replace()方法时同时提及调用addToBackStack()方法。 - rahil008
显示剩余2条评论

374

1) fragmentTransaction.addToBackStack(str);

Description - 将此事务添加到后退栈中。这意味着该事务将在提交后记住,并在稍后从堆栈中弹出时撤消其操作。

2) fragmentTransaction.replace(int containerViewId, Fragment fragment, String tag)

Description - 替换已添加到容器中的现有片段。这基本上与为所有当前使用相同containerViewId添加的碎片调用remove(Fragment)然后使用此处给出的相同参数调用add(int,Fragment,String)相同。

3) fragmentTransaction.add(int containerViewId, Fragment fragment, String tag)

Description - 向活动状态添加片段。这个片段还可以选择将其视图(如果Fragment.onCreateView返回非null)放入活动的容器视图中。

什么是替换已存在的片段、向活动状态添加片段和将活动添加到后退栈中的含义?

有一个堆栈,其中保存了所有正在运行状态的活动。碎片属于活动。因此,您可以将它们添加到嵌套在活动中。

您可以在单个活动中组合多个片段以构建多窗格UI,并在多个活动中重复使用片段。当您在不同的布局中定义了您的片段容器时,这实际上非常有用。您只需要将其替换为任何其他布局中的片段。

当您导航到当前布局时,您有该容器的ID以替换它与您想要的片段。

您还可以通过popBackStack()方法返回到后退栈中的上一个片段。为此,您需要使用addToBackStack()将该片段添加到堆栈中,然后使用commit()进行反映。这是与当前顶部的相反顺序。

findFragmentByTag是否搜索通过add / replace方法或addToBackStack方法添加的标记?

findFragmentByTag搜索通过add/replace方法或addToBackStack方法添加的标记。

这取决于您添加标记的方式。然后,它仅通过先前在XML中充气时定义或作为事务中添加时提供的标记找到一个片段。

参考资料:FragmentTransaction


2
那么,在启动活动时,我可以使用replace方法添加片段吗? - Yohanim
没有添加任何片段。 - Yohanim
2
一个片段容器是否可以包含多个片段?如果是,那么replace()方法会如何表现?它会替换该容器中的所有片段吗?还是Android API有一个接受三个参数的方法,即fragmentContainer、新片段和要替换的对象。 - ved
1
@ved 不,它将用当前的片段替换容器中当前存在的所有片段。 - reubenjohn

176

这里有一张图片展示了add()replace()的区别。

enter image description here

add()方法在FragmentContainer中在前一个片段之上不断添加片段。

replace()方法清除所有之前的片段并将其添加到FragmentContainer中。

什么是addToBackStack?

addtoBackStack方法可以与add()和replace()方法一起使用。它在Fragment API中具有不同的作用。

目的是什么?

与Activity API不同,Fragment API默认不带返回按钮导航。如果要返回到以前的片段,则在Fragment中使用addToBackStack()方法。让我们理解两者的区别:

情况1:

getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.fragmentContainer, fragment, "TAG")
            .addToBackStack("TAG")
            .commit();

输入图像描述

案例2:

getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.fragmentContainer, fragment, "TAG")
            .commit();

输入图像描述


60
图片胜过千言万语。 - Sachin Rajput
2
如果在情况1和2中我们使用“替换”而不是“添加”,会发生什么? - Prashant Maheshwari Andro
1
很好的解释。 - Amit raj
2
超级图解释。 - iamcrypticcoder
易于可视化。谢谢! - neo

173

例如,一个活动有2个碎片,我们使用FragmentManager将每个碎片替换/添加到活动中的布局,并使用addToBackStack方法。

使用replace方法:

进入Fragment1

Fragment1: onAttach
Fragment1: onCreate
Fragment1: onCreateView
Fragment1: onActivityCreated
Fragment1: onStart
Fragment1: onResume

前往 Fragment2

Fragment2: onAttach
Fragment2: onCreate
Fragment1: onPause
Fragment1: onStop
Fragment1: onDestroyView
Fragment2: onCreateView
Fragment2: onActivityCreated
Fragment2: onStart
Fragment2: onResume

弹出片段2

Fragment2: onPause
Fragment2: onStop
Fragment2: onDestroyView
Fragment2: onDestroy
Fragment2: onDetach
Fragment1: onCreateView
Fragment1: onStart
Fragment1: onResume

弹出片段1

Fragment1: onPause
Fragment1: onStop
Fragment1: onDestroyView
Fragment1: onDestroy
Fragment1: onDetach

使用 add 方法

前往 Fragment1

Fragment1: onAttach
Fragment1: onCreate
Fragment1: onCreateView
Fragment1: onActivityCreated
Fragment1: onStart
Fragment1: onResume

前往 Fragment2

Fragment2: onAttach
Fragment2: onCreate
Fragment2: onCreateView
Fragment2: onActivityCreated
Fragment2: onStart
Fragment2: onResume

弹出片段2

Fragment2: onPause
Fragment2: onStop
Fragment2: onDestroyView
Fragment2: onDestroy
Fragment2: onDetach

弹出片段1

Fragment1: onPause
Fragment1: onStop
Fragment1: onDestroyView
Fragment1: onDestroy
Fragment1: onDetach

示例项目


1
每次 Pop 操作不是应该先调用 onPause(),再调用 onStop() 吗? - iCantC
很好的回答,区分了'add()'和'replace()'之间的差异,但没有提到'addToBackStack()'。点赞! - Shirish Herwade
1
@ShirishHerwade 我相信他演示了在两种情况下使用addToBackStack的add和replace之间的区别。 - CyberShark
1
这是一个比仅仅理论更好的答案。 - gtxtreme
比理论更容易记忆 - Zaeem Sattar

60
尽管这是一个早已得到答案的问题,但也许下面的例子可以补充接受的答案,并对像我这样的新安卓程序员有所帮助。
选项1 - “addToBackStack()”从未被使用
情况1A-添加,删除和单击后退按钮。
Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
add Fragment C :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
remove Fragment C :     onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Fragment B is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               App is closed, nothing is visible

案例1B - 添加、替换和单击“返回”按钮

Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
(replace Fragment C)    
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()
Fragment C :        onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()
Fragment C :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               App is closed, nothing is visible

选项二 - 总是使用"addToBackStack()"

情况2A - 添加、删除和单击返回按钮

Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
add Fragment C :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
remove Fragment C :     onPause() - onStop() - onDestroyView()                              Fragment B is visible
(Back button clicked)
Fragment C :        onCreateView() - onActivityCreated() - onStart() - onResume()                   Fragment C is visible
(Back button clicked)
Fragment C :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Fragment B is visible
(Back button clicked)
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Fragment A is visible
(Back button clicked)
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Activity is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()                              App is closed, nothing is visible

案例2B - 添加,替换,删除和单击“后退”按钮

Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
(replace Fragment C)    
Fragment B :        onPause() - onStop() - onDestroyView()  
Fragment A :        onPause() - onStop() - onDestroyView() 
Fragment C :        onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
remove Fragment C :     onPause() - onStop() - onDestroyView()                              Activity is visible
(Back button clicked)
Fragment C :        onCreateView() - onActivityCreated() - onStart() - onResume()                   Fragment C is visible
(Back button clicked)
Fragment C :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               
Fragment A :        onCreateView() - onActivityCreated() - onStart() - onResume()   
Fragment B :        onCreateView() - onActivityCreated() - onStart() - onResume()                   Fragment B is visible
(Back button clicked)
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Fragment A is visible
(Back button clicked)
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Activity is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()                              App is closed, nothing is visible

选项三 - 不总是使用"addToBackStack()"(在下面的示例中,w/o表示未使用)

情况3A - 添加、删除和点击返回按钮

Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B w/o:     onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
add Fragment C w/o:     onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
remove Fragment C :     onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Fragment B is visible
(Back button clicked)
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Activity is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()                              App is closed, nothing is visible

第 3B 案例 - 添加、替换、删除和点击“返回”按钮

Activity :      onCreate() - onStart() - onResume()                             Activity is visible
add Fragment A :    onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment A is visible
add Fragment B w/o:     onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment B is visible
(replace Fragment C)    
Fragment B :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()   
Fragment A :        onPause() - onStop() - onDestroyView() 
Fragment C :        onAttach() - onCreate() - onCreateView() - onActivityCreated() - onStart() - onResume()     Fragment C is visible
remove Fragment C :     onPause() - onStop() - onDestroyView()                              Activity is visible
(Back button clicked)
Fragment C :        onCreateView() - onActivityCreated() - onStart() - onResume()                   Fragment C is visible
(Back button clicked)
Fragment C :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               
Fragment A :        onCreateView() - onActivityCreated() - onStart() - onResume()                   Fragment A is visible
(Back button clicked)
Fragment A :        onPause() - onStop() - onDestroyView() - onDestroy() - onDetach()               Activity is visible
(Back button clicked)
Activity :      onPause() - onStop() - onDestroy()                              App is closed, nothing is visible

那么我们可以说,在使用片段时,返回按钮的工作方式类似于FragmentManager.popBackStack()函数吗? - Tintin

38

add()replace()的基本区别可以描述为:

  • add()仅用于将片段添加到某个根元素。
  • replace()的行为相似,但首先会删除先前的片段,然后再添加下一个片段。

当我们在add()replace()中使用addToBackStack()时,我们可以看到确切的差异。

add()的情况下,如果我们按下返回按钮... onCreateView从未被调用,但在replace()的情况下,当我们按下返回按钮时... onCreateView每次都会被调用。


2
那么,由于之前的片段视图未被销毁,add() 方法是否会导致更多的 Android 内存负担? - Derekyy
1
@Derekyy 是的,我也这么认为。 - Arpit J.
@Derekyy 这取决于使用情况。如果有很高的可能性用户会导航到上一个屏幕,那么我认为替换将对内存造成更大的负担。 - Neeraj Bagra

7

需要注意的重要事项:

Replace和Replace with backstack之间的区别在于,当我们仅使用replace时,片段会被销毁(调用ondestroy()),而当我们使用replace with backstack时,片段的onDestroy()不会被调用(即当按下返回按钮时,片段会通过其onCreateView()方法重新显示)。


这就是为什么人们在这种情况下使用 fragment.getViewLifeCycle() 的原因: https://developer.android.com/guide/fragments/lifecycle#states - Kirill Vashilo

2

当我们使用add()方法添加第一个Fragment --> 第二个Fragment时的最初回答

 btn_one.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(),"Click First 
Fragment",Toast.LENGTH_LONG).show();

                Fragment fragment = new SecondFragment();
                getActivity().getSupportFragmentManager().beginTransaction()
                        .add(R.id.fragment_frame, fragment, fragment.getClass().getSimpleName()).addToBackStack(null).commit();
//                        .replace(R.id.fragment_frame, fragment, fragment.getClass().getSimpleName()).addToBackStack(null).commit();

            }
        });

当我们在片段中使用add()函数
E/Keshav SecondFragment: onAttach
E/Keshav SecondFragment: onCreate
E/Keshav SecondFragment: onCreateView
E/Keshav SecondFragment: onActivityCreated
E/Keshav SecondFragment: onStart
E/Keshav SecondFragment: onResume

当我们在片段中使用replace()方法

在First Fragment中使用replace()方法从第一个片段到第二个片段的过程中,进入Second Fragment

最初的回答:

 btn_one.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(),"Click First Fragment",Toast.LENGTH_LONG).show();

                Fragment fragment = new SecondFragment();
                getActivity().getSupportFragmentManager().beginTransaction()
//                        .add(R.id.fragment_frame, fragment, fragment.getClass().getSimpleName()).addToBackStack(null).commit();
                        .replace(R.id.fragment_frame, fragment, fragment.getClass().getSimpleName()).addToBackStack(null).commit();

            }
        });

E/Keshav SecondFragment: onAttach
E/Keshav SecondFragment: onCreate

E/Keshav FirstFragment: onPause -------------------------- FirstFragment
E/Keshav FirstFragment: onStop --------------------------- FirstFragment
E/Keshav FirstFragment: onDestroyView -------------------- FirstFragment

E/Keshav SecondFragment: onCreateView
E/Keshav SecondFragment: onActivityCreated
E/Keshav SecondFragment: onStart
E/Keshav SecondFragment: onResume

当进行Replace First Fragment时,这些方法会被额外调用( onPause、onStop、onDestroyView 也会被额外调用)。

E/Keshav FirstFragment: onPause

E/Keshav FirstFragment: onStop

E/Keshav FirstFragment: onDestroyView

注:原始答案翻译为“最初的回答”。


0
FragmentManger的add和replace函数可以描述为以下内容: 1. add表示将片段添加到片段返回堆栈中,并显示在您提供的指定框架中,例如:

getFragmentManager.beginTransaction.add(R.id.contentframe,Fragment1.newInstance(),null)

2.replace的意思是在给定的帧上用另一个片段替换该片段

getFragmentManager.beginTransaction.replace(R.id.contentframe,Fragment1.newInstance(),null)

两者之间的主要区别在于,当您回退堆栈时,替换将刷新片段,但添加不会刷新先前的片段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接