在新的菜单提供程序 API 弃用“setHasOptionsMenu”后,隐藏 Fragment 中的菜单项并在导航返回上再次显示它们

2024-06-02

大约一个月前,Android 团队弃用了onCreateOptionsMenu and onOptionsItemSelected, 也setHasOptionsItemMenu。不幸的是,这破坏了我所有的代码。

我的应用程序有很多片段,当用户导航到它们时,我总是确保菜单项会消失并在返回时重新出现,代码如下:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setHasOptionsMenu(true)
}
override fun onPrepareOptionsMenu(menu: Menu) {
    super.onPrepareOptionsMenu(menu)
    menu.clear()
}

这段代码运行良好并且非常简单。现在 Android 团队已弃用(为什么?)setHasOptionsMenu,我无法重新创建此代码。

我了解用于膨胀菜单项和处理菜单项单击事件的新语法,尽管我一生都无法弄清楚如何将菜单隐藏在片段中,然后使用新菜单在导航返回时再次显示它提供商 API。

这是我尝试过的:

导航到片段:

if (supportFragmentManager.backStackEntryCount == 0) {
            supportFragmentManager.commit {
                replace(R.id.activityMain_primaryFragmentHost, NewProjectFragment.newInstance(mainSpotlight != null))
                addToBackStack(null)
            }
        }

getRootMenuProvider函数于ActivityFragment界面:

interface ActivityFragment {
    val title: String

    companion object {
        fun getRootMenuProvider() = object : MenuProvider {
            override fun onPrepareMenu(menu: Menu) {
                for (_menuItem in menu.children) {
                    _menuItem.isVisible = false
                }
            }

            override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
            }

            override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
                return false
            }
        }
    }
}

使用getRootMenuProvider功能:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val menuHost: MenuHost = requireActivity()
        menuHost.addMenuProvider(ActivityFragment.getRootMenuProvider())
    }

MainActivity(尝试将菜单项恢复到之前的状态):

    override fun onPrepareOptionsMenu(menu: Menu): Boolean {
        for (_menu in menu.children) {
            _menu.isVisible = true
        }

        return super.onPrepareOptionsMenu(menu)
    }

    override fun onBackPressed() {
        super.onBackPressed()
        findViewById<BottomNavigationView>(R.id.activityMain_bottomNavigationView)?.visibility = View.VISIBLE
        invalidateOptionsMenu()
    }

这会隐藏片段中的项目,但导航回来后这些项目仍然保持隐藏状态,直到用户通过旋转屏幕或执行类似操作重新加载活动。

如何隐藏片段中的菜单项并在导航返回时重新显示它们使用新的菜单提供程序 API?


短期

一切都“崩溃”的原因是因为你假设menu.clear()并且片段菜单调用的调度发生在您的活动添加了自己的菜单项之后。现在,当您的活动调用时,片段会经历菜单调用的调度super.onCreateOptionsMenu() or super.onPrepareOptionsMenu()通常你可以通过将其作为last你的重写调用的东西,而不是第一个。

长期

事实上,您做错了很多:由 Activity 控制的全局菜单是共享资源,任何单独的片段都不应该手动清除整个菜单。这会破坏活动菜单项、子片段菜单项以及其他片段的菜单项。只有膨胀某些菜单项的组件才应该接触这些特定的菜单项。

因此,要解决您的问题,您应该遵循活动 1.4.0-alpha01 发行说明 https://developer.android.com/jetpack/androidx/releases/activity#1.4.0-alpha01(添加了MenuHost and MenuProvider集成到 Activity 层:

安卓XComponentActivity[及其子类FragmentActivity and AppCompatActivity] 现在实现了MenuHost界面。这允许任何组件将菜单项添加到ActionBar通过添加一个MenuProvider活动的实例。Each MenuProvider可以选择添加一个生命周期,该生命周期将根据Lifecycle说明并处理删除MenuProvider当。。。的时候Lifecycle被摧毁了。

他们继续展示其在片段中的用法示例:

/**
  * Using the addMenuProvider() API in a Fragment
  **/
ExampleFragment : Fragment(R.layout.fragment_example) {

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    // The usage of an interface lets you inject your own implementation
    val menuHost: MenuHost = requireActivity()
  
    // Add menu items without using the Fragment Menu APIs
    // Note how we can tie the MenuProvider to the viewLifecycleOwner
    // and an optional Lifecycle.State (here, RESUMED) to indicate when
    // the menu should be visible
    menuHost.addMenuProvider(object : MenuProvider {
      override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
        // Add menu items here
        menuInflater.inflate(R.menu.example_menu, menu)
      }

      override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
        // Handle the menu selection
        return true
      }
    }, viewLifecycleOwner, Lifecycle.State.RESUMED)
  }

这特别展示了三件事:

  1. 单个MenuProvider应该只触摸其菜单项。您永远不应该“清除所有菜单项”或任何影响其他组件菜单项的内容。
  2. 通过致电addMenuProvider具有生命周期(在本例中,是片段视图的生命周期 - 即仅当片段视图在屏幕上时才存在),那么您自动地当您的片段视图被破坏时隐藏菜单项(当您的replace发生呼叫)和自动地当片段的视图重新出现时(即弹出后堆栈时)重新显示。
  3. That 片段本身也就是控制着Lifecycle菜单项的可见性应该是创建和处理其自己的菜单项的可见性。您的活动(可以添加自己的活动)MenuProvider如另一个示例中所示)应该只添加活动整个生命周期中存在的菜单项(在all片段)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在新的菜单提供程序 API 弃用“setHasOptionsMenu”后,隐藏 Fragment 中的菜单项并在导航返回上再次显示它们 的相关文章

  • Android正则表达式-返回匹配的字符串

    在我的 Android 项目中 我有一个正则表达式和一个字符串 其中应该有匹配的表达式 问题是我只找到了一个matches 方法 返回布尔值 有没有什么东西 只返回匹配的字符串 例如 如果我的字符串是 中午 12 点去商店 我想检查该字符串
  • 以编程方式在 Android 上运行 ps shell 命令

    我正在尝试在我的 Android 应用程序上执行 ps 命令 如下所示 try Process process Runtime getRuntime exec ps BufferedReader reader new BufferedRea
  • 如何从 JSON 响应中获取所选微调项目的 ID?

    Outline 我必须从服务器获取一些运营商列表 下面是我的 JSON 数据 PrepaidServiceList operator id 2 operator name Reliance GSM operator id 9 operato
  • 无法绑定到调试器的本地 XXXX

    我不断得到Can t bind to local XXXX for debugger控制台中的消息 但不适用于 1 个端口 适用于所有随机端口 我已经完成了中所述的操作这个问题 https stackoverflow com questio
  • CollapsingToolbarLayout 无法识别滚动 fling

    我创建了一个简单的折叠工具栏布局它就像一个魅力 我的问题是 如果我尝试在嵌套滚动视图 当我松开手指时它就会停止 正常的滚动就像它应该的那样工作 我的活动代码是不变 gt 自动生成空活动 我只是单击了 android studio 中的 创建
  • 将长文本分成页面供viewpager使用

    我正在实施中的messureText方法这个问题 https stackoverflow com a 22199847 3930169在 viewpager 中渲染之前将长文本分成指定大小的页面 我正在使用增量字符数进行 while 循环以
  • 覆盖服务 - 按下返回按钮

    我怎样才能做到这一点 目前的解决方案 我启动了一个透明的活动 捕获后按 将其转发到我的服务 然后自行关闭 但此活动将在当前正在运行的活动中可见 因此这不是一个非常漂亮的解决方案 看到的解决方案 我见过一个应用程序确实可以捕获服务中的后按 而
  • 尝试在空对象引用上调用接口方法“...”

    我正在开发一个具有蓝牙功能的应用程序 我使用片段来扫描并列出蓝牙设备 单击时 会回调提供所选蓝牙设备的主要活动 我从使用 Android 6 API 23 的智能手机开始 然后必须调整代码以用于 Android 5 0 API 21 我刚刚
  • Android 如何知道手电筒是否打开

    我正在使用 CameraManager 和 CameraCharacteristics 我想知道如何检查手电筒是否打开 试试这段代码 public boolean FlashStatus Camera Parameters paramete
  • 如何在改造中在主体内传递 JSON 数组

    intent sale redirect urls return url http example com your redirect url html cancel url http example com your cancel url
  • SecurityException:Parcel.readException 来自谷歌分析代码

    我们的应用程序变得有很多不同SecurityException来自我们的崩溃报告软件的报告 这是崩溃的堆栈跟踪 java lang SecurityException Unable to find app for caller androi
  • 从设备获取日期并将其转换为 GMT+4

    我正在尝试查找 Android 手机的时区 因为我想获取日期对象 但我想要 GMT 4 格式 我看到的所有其他答案都会转换来自 API 请求的时间 其时区已知 我怎样才能做到这一点 其他方法可能是将服务器传来的 GMT 4 时间转换为我设备
  • SQLite CursorWindow 限制 - 如何避免崩溃

    我必须执行查询并将结果存储在列表中 我使用的函数如下 List
  • 无法启动由 ContextImpl.openFileOutput 处的 NullPointerException 引起的活动

    我发布了一个使用 Google 地图 Android 兼容性库的应用程序 https github com petedoyle android support v4 googlemaps https github com petedoyle
  • 适用于标准类兼容 USB 设备的 Android USB 主机模式“软模式”驱动程序

    现在既然Android API 支持直接使用 USB 设备 http developer android com guide topics connectivity usb host html 自 3 1 起 我很好奇是否有任何工作可以为一
  • 面临 process.start(); 的问题在 Android 棒棒糖中

    面临一个问题process start 在 Android 棒棒糖中 我在服务中遇到了 android lollipop 后台进程的问题 我的代码在 KitKat 之前工作正常 我有一个ProcessBuilder pBuilder并向其中
  • 菜单项标题未显示

    菜单项的标题未显示在片段内 我在菜单文件中有两个项目 第一个是带有图标和标签的showAsAction always在工具栏中显示图标 第二个只有标题 我不知道这里出了什么问题 菜单项的所有操作均有效 例如下面 菜单 销售 xml menu
  • 让 DrawerLayout 在 ActionBar 上滑动

    我在活动中有一个滑动抽屉菜单 其中有一个带有一些选项卡的操作栏 我想让滑动抽屉滑过标签 而不是滑过标签下方 这就是现在的样子 关于如何做到这一点有什么想法吗 注意 我知道我可能会在这里打破一些约定和 UI 模式 如果它根本不起作用 我会考虑
  • Android Studio - 程序类型已存在:com.google.android.gms.internal.measurement.zzwp

    昨天 我的应用程序运行良好 今天 不知道为什么 重新打开Android Studio后 应用程序就无法编译了 显示的错误是 Program type already present com google android gms intern
  • ACRA formkey 哪里可以得到?

    所以我尝试按照以下说明进行操作https github com ACRA acra wiki BasicSetup https github com ACRA acra wiki BasicSetup但它太旧了或者什么的 使用我自己的 gm

随机推荐