从后台堆栈中弹出时,片段的onResume()方法未被调用

32

您好,我正在开发一个安卓应用程序,其中使用了单一Activity和3个片段。假设我有3个片段A B C。当我从A切换到B时,我会将Fragment添加到后退堆栈中,从B到C同样如此。现在,当我从C返回时,它会显示B,并且从B到A也是这样。

但是问题是当我从C到B或从B到A时,它没有调用onResume()或任何其他Fragment的生命周期方法。

实际上,我想要为每个Fragment都有不同的ActionBar标题。因此,在我的代码中,当我从A移动到B或从B到C时,我正在更改片段内的活动标题。但是,当我点击返回时,它没有按照那样改变。

实际问题是什么?为什么从后退堆栈弹出后它不会调用我的FragmentonResume()?我该怎么解决这个问题?需要帮助。谢谢。


仅当活动恢复时,片段的onResume()才会被调用。所以这对你没有帮助。我现在也面临类似的问题。您可以实现OnBackStackChangedListener并获取堆栈顶部的片段名称,并根据该名称设置操作栏标题。 - LoveMeSomeFood
8个回答

25

FragmentonResume()方法只会在Activity恢复时调用,所以这并不能帮助你。我现在也遇到了类似的问题。您可以实现OnBackStackChangedListener接口,获取堆栈顶部的片段名称,并根据它来设置ActionBar的标题。

private FragmentManager.OnBackStackChangedListener getListener()
{
    FragmentManager.OnBackStackChangedListener result = new FragmentManager.OnBackStackChangedListener()
    {
        public void onBackStackChanged()
        {
            FragmentManager manager = getFragmentManager();

            if (manager != null)
            {
                if(manager.getBackStackEntryCount() >= 1){
                    String topOnStack = manager.getBackStackEntryAt(manager.getBackStackEntryCount()-1).getName();
                    Log.i("TOP ON BACK STACK",topOnStack);
                }
                }
            }
    };

    return result;
}

这也是我正在使用的。 - Radu

15

尝试在FragmentTransaction上使用replace方法而不是add方法。这对我很有效:

FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment);
ft.addToBackStack(null);
ft.commit();

2
这个可行。可能现在应该成为被接受的答案。 - RajV
1
当Activity的onResume()或onPause()被调用时,片段的onResume()或onPause()也将被调用。它们与Activity紧密耦合。如果您调用replace,则会调用onStop()。 - Zar E Ahmer
替换片段将重新创建UI并执行在onCreateView和onViewCreated方法中编写的业务逻辑。在使用此方法之前要小心。 - Khawar Raza

9
正如其他人已经说过的那样,onResume()只有在活动本身恢复时才会被调用,因此这完全没有帮助。
您需要检查是添加新片段还是替换现有片段:
  • 如果您replace()前一个片段,则当您返回到它时,将从头开始重新创建该前一个片段,因此onCreateView()将再次被调用,您可以在那里更新工具栏标题。 您可能已经这样做了。

  • 如果您add()新片段,则之前的片段仍然存在,只是不可见。 当您返回到它时,由您从后退堆栈中获取最后一个条目(使用片段管理器中的getBackStackEntryCount()getBackStackEntryAt()),获取相应的Fragment对象(使用片段管理器中的findFragmentByTag()),将该Fragment强制转换为所有片段都将继承的某个基类,并在该片段上调用自定义方法,例如onVisible()。 在您的基类中,onVisible()的默认实现不执行任何操作。 在每个片段中重写以根据需要更新工具栏标题、FAB和任何其他内容。 我也从onResume()调用onVisible(),以避免代码重复。


4
您可以使用片段化的方法:
@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
}

1
但是当我从堆栈中弹出片段时,它不会调用这个。 - Salut Amigo

2
我使用这种方式,在你的代码片段中添加以下代码块。
requireActivity().supportFragmentManager.addOnBackStackChangedListener {
            val fm = requireActivity().supportFragmentManager
            fm?.let {
                if (it.backStackEntryCount == YOUR_FRAGMENT_BACK_STACK_INDEX) {
                    // your fragment visible
                }
            }
        }

这是最佳答案。相信我 :) - porya74

2
将此代码放入您的片段中。
@Override
public void setUserVisibleHint(boolean visible) {
        super.setUserVisibleHint(visible);
        if (visible && isResumed()) {
            onResume();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!getUserVisibleHint()) {
            return;
        }
        setData();
    }

0
其他答案依赖于特定的片段事务被命名,或者有一个带标记的片段,依赖于已弃用的方法,或者将逻辑放在Activity中。这通过使用其视图检测片段在返回堆栈上的可见性来检测,对于我的需求,这已经足够接近onResume了。由于这涉及到位于返回堆栈的顶部,因此需要这样做。
视图扩展以检测视图是否显示在屏幕上:(另请参见如何在Android上判断视图是否可见?
fun View.isVisibleOnScreen(): Boolean {
    if (!isShown) return false
    val actualPosition = Rect().also { getGlobalVisibleRect(it) }
    val screenWidth = Resources.getSystem().displayMetrics.widthPixels
    val screenHeight = Resources.getSystem().displayMetrics.heightPixels
    val screen = Rect(0, 0, screenWidth, screenHeight)
    return Rect.intersects(actualPosition, screen)
}

然后从片段中定义了一个返回栈侦听器,监视堆栈上的顶部片段(最后添加的片段)。

fun Fragment.setOnFragmentStackVisibilityListener(onVisible: () -> Unit) {
    val renderDelayMillis = 300L
    parentFragmentManager.addOnBackStackChangedListener {
        Handler(Looper.getMainLooper()).postDelayed({
            if (isAdded) {
                val topStackFragment = parentFragmentManager.fragments[parentFragmentManager.fragments.size - 1]
                if (topStackFragment.view == view && isVisible && view!!.isVisibleOnScreen()) {
                    onVisible.invoke()
                }
            }
        }, renderDelayMillis)
    }
}

返回堆栈监听器在视图准备就绪之前被调用,因此需要任意小的延迟。


-1
尝试在 Fragment 的 onCreateView() 中更改标题。

但是当我从堆栈中弹出片段时,它不会调用任何生命周期方法。那么我该怎么办呢?谢谢您的帮助。 - nilkash
您需要在片段的onCreateView方法中更改标题。当您将片段弹出或添加到堆栈中时,onCreateView方法会被调用,然后您可以根据片段类设置标题。 - Harshit Rathi
@HarshitRathi 我尝试了一下,就像 @nilkash 说的那样,当一个片段从后退栈中弹出并且新的片段变得可见时,onCreateView()不会被调用。 - Franco

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