Android 4.2: 带嵌套片段的回栈行为

在 Android 4.2中,支持库获得了对嵌套片段 看这里的支持。我已经玩了它,并发现了一个有趣的行为/错误,关于回栈和 GetChildFragmentManager ()。当使用 getChildFragmentManager ()和 addToBackStack (String name)时,通过按后退按钮,系统不会沿着后退堆栈运行到前一个片段。 另一方面,当使用 GetFragmentManager ()和 addToBackStack (String 名称)时,通过按后退按钮,系统返回到前面的片段。

对我来说,这种行为是出乎意料的。通过按下设备上的后退按钮,我希望最后添加到后退堆栈中的片段将被弹出,即使该片段被添加到子片段管理器中的后退堆栈中。

这种行为是正确的吗? 这种行为是错误的吗? 有没有解决这个问题的方法?

使用 getChildFragmentManager ()的示例代码:

public class FragmentceptionActivity extends FragmentActivity {


@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);


final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;


final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);


setContentView(wrapper1);


getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}


public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);


return wrapper2;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}


public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);


return wrapper3;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}


public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);


return wrapper4;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}


public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);


return wrapper5;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}


}

使用 getFragmentManager ()的示例代码:

public class FragmentceptionActivity extends FragmentActivity {


@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);


final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;


final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);


setContentView(wrapper1);


getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}


public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);


return wrapper2;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}


public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);


return wrapper3;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}


public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);


return wrapper4;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);


getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}


public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);


final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;


final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);


return wrapper5;
}


@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}


}
47860 次浏览

看起来像只虫子。 请看: Http://code.google.com/p/android/issues/detail?id=40323

对于一个我已经成功使用的变通方法(如评论中所建议的) :

    @Override
public void onBackPressed() {


// If the fragment exists and has some back-stack entry
if (mActivityDirectFragment != null && mActivityDirectFragment.getChildFragmentManager().getBackStackEntryCount() > 0){
// Get the fragment fragment manager - and pop the backstack
mActivityDirectFragment.getChildFragmentManager().popBackStack();
}
// Else, nothing in the direct fragment back stack
else{
// Let super handle the back press
super.onBackPressed();
}
}

原因是您的 Activity 派生自 FragmentActivity,它处理 BACK 键按下(参见 FragmentActivity的第173行)。

In our application, I'm using a ViewPager (with fragments) and each fragment can have nested fragments. The way I've handled this is by:

  • 用单个方法 void onBackKeyPressed()定义 OnBackKeyPresseListener 接口
  • 在 ViewPager 显示的“ top”片段中实现了这个接口
  • 重写 onKeyDown 并检测 BACK Press,并在视图页导航中的当前活动片段中调用 onBackKeyPress。

还要注意,我在片段中使用了 getChildFragmentManager()来正确地嵌套片段。您可以在 这个机器人开发者的帖子中看到讨论和解释。

我能够通过在 onCreate View ()方法中向父片段添加这个方法并传递根视图来处理片段回栈。

private void catchBackEvent(View v){
v.setFocusableInTouchMode(true);
v.requestFocus();
v.setOnKeyListener( new OnKeyListener()
{
@Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
if(isEnableFragmentBackStack()){
getChildFragmentManager().popBackStack();
setEnableFragmentBackStack(false);
return true;
}
else
return false;
}
return false;
}
} );
}

方法 isEnableFragmentBackStack ()是一个布尔标志,用于知道何时处于主片段或下一个片段上。

确保在提交需要堆栈的片段时,必须添加 addToBackstack 方法。

如果你有一个 DialogFragment,它反过来又有嵌套的片段,“解决办法”是有点不同。与设置 onKeyListenerrootView不同,您需要对 Dialog进行这样的设置。另外,您将设置一个 DialogInterface.OnKeyListener,而不是 View的一个。当然,记住 addToBackStack

顺便说一下,在后台堆栈上有一个片段用于将调用委托回活动,这是我的个人用例。典型的情况可能是计数为0。

下面是您在 onCreateDialog 中要做的事情

    @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog =  super.onCreateDialog(savedInstanceState);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
FragmentManager cfm = getChildFragmentManager();
if(cfm.getBackStackEntryCount()>1){
cfm.popBackStack();
return true;
}
}
return false;
}
});
return dialog;
}

这个解决方案可能是“肖恩回答:

@Override
public void onBackPressed() {
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
FragmentManager fm = getSupportFragmentManager();
for (Fragment frag : fm.getFragments()) {
if (frag.isVisible()) {
FragmentManager childFm = frag.getChildFragmentManager();
if (childFm.getBackStackEntryCount() > 0) {
childFm.popBackStack();
return;
}
}
}
super.onBackPressed();
}

同样,我准备这个解决方案的基础上@肖恩回答以上。

正如@AZ13所说,这个解决方案只在一个级别的子片段情况下可行。在多级碎片的情况下,工作变得有点复杂,所以我建议只在我说过的可行情况下尝试这个解决方案。=)

注意: 由于 getFragments方法现在是私有方法,所以这个解决方案不能工作。您可以检查一个链接的注释,该链接提供了有关这种情况的解决方案。

这个解决方案可能是@ismailarilik 的更好版本答案:

嵌套片段版本

private boolean onBackPressed(FragmentManager fm) {
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}


List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
if (onBackPressed(frag.getChildFragmentManager())) {
return true;
}
}
}
}
}
return false;
}


@Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (onBackPressed(fm)) {
return;
}
super.onBackPressed();
}

With this answer it will handle recursive back checking and give each fragment the chance to override the default behaviour. This means you can have a fragment that hosts a ViewPager do something special like scroll to the page that as a back-stack, or scroll to the home page and then on the next back press exit.

将此添加到扩展 AppCompatActivity 的活动中。

@Override
public void onBackPressed()
{
if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
super.onBackPressed();
}
}

将它添加到 BaseFragment 或者可以让所有片段继承的类中。

public static boolean handleBackPressed(FragmentManager fm)
{
if(fm.getFragments() != null){
for(Fragment frag : fm.getFragments()){
if(frag != null && frag.isVisible() && frag instanceof BaseFragment){
if(((BaseFragment)frag).onBackPressed()){
return true;
}
}
}
}
return false;
}


protected boolean onBackPressed()
{
FragmentManager fm = getChildFragmentManager();
if(handleBackPressed(fm)){
return true;
}
else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
fm.popBackStack();
return true;
}
return false;
}

对于儿童碎片,这个工作. 。

@Override
public void onBackPressed() {


if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
doExit(); //super.onBackPressed();
}
}

这段代码将在片段管理器树中导航,并返回添加的最后一个片段,其中包含任何可以从堆栈中弹出的片段:

private FragmentManager getLastFragmentManagerWithBack(FragmentManager fm)
{
FragmentManager fmLast = fm;


List<Fragment> fragments = fm.getFragments();


for (Fragment f : fragments)
{
if ((f.getChildFragmentManager() != null) && (f.getChildFragmentManager().getBackStackEntryCount() > 0))
{
fmLast = f.getFragmentManager();
FragmentManager fmChild = getLastFragmentManagerWithBack(f.getChildFragmentManager());


if (fmChild != fmLast)
fmLast = fmChild;
}
}


return fmLast;
}

Call the method:

@Override
public void onBackPressed()
{
FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());


if (fm.getBackStackEntryCount() > 0)
{
fm.popBackStack();
return;
}


super.onBackPressed();
}

这个解决方案可能更好,因为它检查嵌套片段的所有级别:

 /**
* This method will go check all the back stacks of the added fragments and their nested fragments
* to the the {@code FragmentManager} passed as parameter.
* If there is a fragment and the back stack of this fragment is not empty,
* then emulate 'onBackPressed' behaviour, because in default, it is not working.
*
* @param fm the fragment manager to which we will try to dispatch the back pressed event.
* @return {@code true} if the onBackPressed event was consumed by a child fragment, otherwise {@code false}.
*/
public static boolean dispatchOnBackPressedToFragments(FragmentManager fm) {


List<Fragment> fragments = fm.getFragments();
boolean result;
if (fragments != null && !fragments.isEmpty()) {
for (Fragment frag : fragments) {
if (frag != null && frag.isAdded() && frag.getChildFragmentManager() != null) {
// go to the next level of child fragments.
result = dispatchOnBackPressedToFragments(frag.getChildFragmentManager());
if (result) return true;
}
}
}


// if the back stack is not empty then we pop the last transaction.
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
fm.executePendingTransactions();
return true;
}


return false;
}

in your activity onBackPressed you can simply call it this way:

FragmentManager fm = getSupportFragmentManager();
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
if (!dispatchOnBackPressedToFragments(fm)) {
// if no child fragment consumed the onBackPressed event,
// we execute the default behaviour.
super.onBackPressed();
}

感谢大家的帮助,这个(调整后的版本)适合我:

@Override
public void onBackPressed() {
if (!recursivePopBackStack(getSupportFragmentManager())) {
super.onBackPressed();
}
}


/**
* Recursively look through nested fragments for a backstack entry to pop
* @return: true if a pop was performed
*/
public static boolean recursivePopBackStack(FragmentManager fragmentManager) {
if (fragmentManager.getFragments() != null) {
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null && fragment.isVisible()) {
boolean popped = recursivePopBackStack(fragment.getChildFragmentManager());
if (popped) {
return true;
}
}
}
}


if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
return true;
}


return false;
}

注意: 您可能还想将这些嵌套片段的背景颜色设置为应用程序主题的窗口背景颜色,因为默认情况下它们是透明的。有点超出了这个问题的范围,但是它是通过解析属性 android.R.attr.windowBack 并将片段视图的背景设置为该资源 ID 来实现的。

5年多了,这个问题仍然相关。如果由于受到限制,您不想使用 bragmentManager.getFragments ()。扩展和使用以下类:

Java

abstract public class NestedFragmentActivity extends AppCompatActivity {


private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
private final Stack<String> mActiveFragmentTagStack = new Stack<>();


@Override
public void onBackPressed() {
if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {


// Find by id
int lastFragmentId = mActiveFragmentIdStack.lastElement();
NestedFragment nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentById(lastFragmentId);


// If cannot find by id, find by tag
if (nestedFragment == null) {
String lastFragmentTag = mActiveFragmentTagStack.lastElement();
nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
}


if (nestedFragment != null) {
nestedFragment.onBackPressed();
}


// If cannot find by tag, then simply pop
mActiveFragmentTagStack.pop();
mActiveFragmentIdStack.pop();


} else {
super.onBackPressed();
}
}


public void addToBackStack(int fragmentId, String fragmentTag) {
mActiveFragmentIdStack.add(fragmentId);
mActiveFragmentTagStack.add(fragmentTag);
}
}

Java

abstract public class NestedFragment extends Fragment {


private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
private final Stack<String> mActiveFragmentTagStack = new Stack<>();


private NestedFragmentActivity mParentActivity;
private NestedFragment mParentFragment;


@Override
public void onAttach(Context context) {
super.onAttach(context);


if (getParentFragment() == null) {
try {
mParentActivity = (NestedFragmentActivity) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + NestedFragmentActivity.class.getName());
}
} else {
try {
mParentFragment = (NestedFragment) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(getParentFragment().getClass().toString()
+ " must implement " + NestedFragment.class.getName());
}
}
}


public void onBackPressed() {


if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {


// Find by id
int lastFragmentId = mActiveFragmentIdStack.lastElement();
NestedFragment nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentById(lastFragmentId);


// If cannot find by id, find by tag
if (nestedFragment == null) {
String lastFragmentTag = mActiveFragmentTagStack.lastElement();
nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentByTag(lastFragmentTag);
}


if (nestedFragment != null) {
nestedFragment.onBackPressed();
}


// If cannot find by tag, then simply pop
mActiveFragmentIdStack.pop();
mActiveFragmentTagStack.pop();


} else {
getChildFragmentManager().popBackStack();
}
}


private void addToBackStack(int fragmentId, String fragmentTag) {
mActiveFragmentIdStack.add(fragmentId);
mActiveFragmentTagStack.add(fragmentTag);
}


public void addToParentBackStack() {
if (mParentFragment != null) {
mParentFragment.addToBackStack(getId(), getTag());
} else if (mParentActivity != null) {
mParentActivity.addToBackStack(getId(), getTag());
}
}
}

说明:

从上述类扩展的每个活动和片段都为它们的每个子类和子类的子类管理自己的回栈,等等。回溯只是“活动片段”标记/id 的记录。因此,需要注意的是始终为片段提供标记和/或 id。

当添加到 children FragmentManager 中的回栈时,还需要调用“ addToParentBackStack ()”。这样可以确保片段的标记/id 被添加到父片段/活动中,用于以后的持久性有机污染物。

例如:

    getChildFragmentManager().beginTransaction().replace(
R.id.fragment,
fragment,
fragment.getTag()
).addToBackStack(null).commit();
addToParentBackStack();

这个问题的真正答案在片段事务的函数 setPrimaryNavigationFragment 中。

/**
* Set a currently active fragment in this FragmentManager as the primary navigation fragment.
*
* <p>The primary navigation fragment's
* {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
* to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
* if no ID or transaction name is provided to pop to. Navigation operations outside of the
* fragment system may choose to delegate those actions to the primary navigation fragment
* as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
*
* <p>The fragment provided must currently be added to the FragmentManager to be set as
* a primary navigation fragment, or previously added as part of this transaction.</p>
*
* @param fragment the fragment to set as the primary navigation fragment
* @return the same FragmentTransaction instance
*/
public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);

当活动添加它时,必须在初始父片段上设置此函数。在我的活动中有一个替换片段函数,它看起来像这样:

public void replaceFragment(int containerId, BaseFragment fragment, boolean addToBackstack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setPrimaryNavigationFragment(fragment);
if (addToBackstack) {
fragmentTransaction.addToBackStack(fragment.TAG);
}


fragmentTransaction.replace(containerId, fragment).commit();
}

这给了你相同的行为,就像你从普通的片段 B 点击回到片段 A,只不过现在它也在子片段上!

我通过在活动属性中保留当前打开的片段来解决这个问题,然后重写方法

 // INSIDE ACTIVITY
override fun onBackPressed() {
val fragment = currentFragment


if(fragment != null && fragment.childFragmentManager.fragments.any()){
fragment.childFragmentManager.popBackStack()
}
else {
super.onBackPressed()
}
}

这就是我如何从内部将子片段添加到当前打开的片段中的方法。

 // INSIDE FRAGMENT
menu_fragment_view.setBackgroundColor(-((Math.random() * 10000 ).toInt() % 30000)) // to see change
add_fragment_button.setOnClickListener {
val transaction = childFragmentManager.beginTransaction()


transaction.add(R.id.menu_fragment_fragment_holder, MenuFragment())


transaction.addToBackStack(null)


transaction.commit()
}

这是父片段和添加片段的 xml 布局

 <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/menu_fragment_view">
<Button
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/add_fragment_button"
android:text="Just add fragment"/>
<FrameLayout
android:id="@+id/menu_fragment_fragment_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

I have implemented it correctly if anybody didnt found any answer till now

just add this method on your child nested fragment

   @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
// This callback will only be called when MyFragment is at least Started.
OnBackPressedCallback callback = new OnBackPressedCallback(true ) {
@Override
public void handleOnBackPressed() {
// Handle the back button event
FragmentManager fm= getFragmentManager();
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
Log.e( "Frag","back" );


}


List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
Log.e( "Frag","Visible" );
}
}
}
}




}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
super.onCreate( savedInstanceState );
}

如果在片段中使用片段,则使用 getChildFragmentManager()

getChildFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

如果您正在使用子片段并替换它,请使用 getParentFragmentManager()

getParentFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

如果两者都不为你工作,试试这个 getActivity().getSupportFragmentManager()

getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();

在观察了这里提出的一些解决方案之后,我发现为了在何时弹出堆栈或者何时回退操作应该被它忽略时允许灵活性和对父片段的控制,我宁愿使用这样的实现:

定义“ ParentFragment”界面:

interface ParentFragment {
/**
* Fragments that host child fragments and want to control their BackStack behaviour when the back button is pressed should implement this
*
* @return true if back press was handled, false otherwise
*/
fun onBackPressed(): Boolean

}

重写父活动(或 BaseActivity)中的“ onBackPress”:

override fun onBackPressed() {
val fm: FragmentManager = supportFragmentManager
for (frag in fm.fragments) {
if (frag.isVisible && frag is ParentFragment && frag.onBackPressed()) {
return
}
}
super.onBackPressed()
}

然后允许父片段按照自己的意愿进行处理,例如:

override fun onBackPressed(): Boolean {
val childFragment = childFragmentManager.findFragmentByTag(SomeChildFragment::class.java.simpleName)
if (childFragment != null && childFragment.isVisible) {
// Only for that case, pop the BackStack (perhaps when other child fragments are visible don't)
childFragmentManager.popBackStack()
return true
}
return false
}

这样就可以避免在使用视图页导航(例如,返回堆栈条目计数 > 0)时认为有一些合法的子片段需要删除。

这是我的解决办法。我发现了一个儿童碎片。然后,该子片段可以递归地在其 children FragmentManager 上创建更多的自身版本。这是我活动的 onBackPress 函数的代码。

override fun onBackPressed() {
// Get the child fragment manager of the displayed fragment
var childFragmentManager = supportFragmentManager.fragments.first().childFragmentManager


// If there are nested fragments, handle those first
if (childFragmentManager.backStackEntryCount > 0) {
// If there are more nested fragments, go deeper into those
var nestedChildFragmentManager = childFragmentManager.fragments.first().childFragmentManager
while (nestedChildFragmentManager.backStackEntryCount > 0) {
childFragmentManager = nestedChildFragmentManager
nestedChildFragmentManager = childFragmentManager.fragments.first().childFragmentManager
}


// Dismiss the innermost nested fragment
childFragmentManager.popBackStack()
} else if (supportFragmentManager.backStackEntryCount > 0) {
// If there are no nested fragments, dismiss the original fragment
supportFragmentManager.popBackStack()
} else {
// Otherwise, handle the back press as normal
super.onBackPressed()
}
}