• Android 动态更新Menu菜单


    1. 需求描述

            Android Menu菜单是比较常见的功能,在ActionBar  or ToolBar上显示,点击更多(3个点),会有下拉列表菜单展示,  在工作项目中有个小需求改动: 在 ToolBar上添加一个图标,点击后会切换图标状态,界面也会显示对应内容,这也是本篇文章要讲的是如何动态更新Menu菜单。

            首先,我们来看看效果图:

    1. 当点击网格图标时,显示为网格模式

     2. 当点击列表图标时,显示为列表模式

     3. 点击更多图标时,显示更多菜单列表:设置   关于  测试

    2. 基础知识

            在写代码之前,先来复习一下Menu的基础知识,具体可以看官方文档:Menus  |  Android Developers

    菜单xml编写例子:

    1. <menu xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:app="http://schemas.android.com/apk/res-auto"
    3. xmlns:tools="http://schemas.android.com/tools"
    4. tools:context="com.example.menutest.MainActivity">
    5. <item
    6. android:id="@+id/sub_menu_grid"
    7. android:title="网格视图"
    8. android:icon="@drawable/ic_menu_view_grid"
    9. app:showAsAction="always"/>
    10. <item
    11. android:id="@+id/sub_menu_list"
    12. android:title="列表视图"
    13. android:icon="@drawable/ic_menu_view_list"
    14. app:showAsAction="always"/>
    15. <item
    16. android:id="@+id/action_settings"
    17. android:orderInCategory="100"
    18. android:title="设置"
    19. app:showAsAction="never" />
    20. <item
    21. android:id="@+id/action_about"
    22. android:orderInCategory="200"
    23. android:title="关于"
    24. app:showAsAction="never" />
    25. <item
    26. android:id="@+id/action_test"
    27. android:orderInCategory="300"
    28. android:title="测试"
    29. app:showAsAction="never" />
    30. </menu>

    是我们主要需要关注的元素,它的常见属性如下:

        android:id:     菜单项(MenuItem)的唯一标识(必须定义)
        android:icon: 菜单项的图标(可选)
        android:title: 菜单项的标题(必选)
        android:showAsAction:指定菜单项的显示方式。常用的有ifRoom、never、always、withText,多个属性值之间可以使用|隔开。

    指定菜单的显示方式:

    always:菜单项永远不会被收纳到溢出菜单中,因此在菜单项过多的情况下可能超出菜单栏的显示范围。

    ifRoom:在空间足够时,菜单项会显示在菜单栏中,否则收纳入溢出菜单中。

    withText:无论菜单项是否定义了icon属性,都只会显示它的标题,而不会显示图标。使用这种方式的菜单项默认会被收纳入溢出菜单中。

    never:菜单项永远只会出现在溢出菜单中。

    我们通过上面的3张图片,在Toolbar上面显示有两类菜单:

    1. 比如网格和列表菜单,在菜单栏上定义为一直显示(always),我们称它为常驻菜单

    2. 另一种会被集中放置到溢出菜单中(就是菜单栏右侧的3个小点图标图标)

    3. 菜单加载

            菜单加载,有两个相关API 

      1.  onCreateOptionsMenu(Menu menu)

          此方法在初次加载菜单时,会调用一次。

    2.  onPrepareOptionsMenu(Menu menu)

          此方法应用场景:在运行时更改菜单项,我们看看官网的描述:

    在运行时修改的选项菜单:

    系统调用onCreateOptionsMenu方法后,将保留创建的Menu实例。除非菜单由于某些原因而失效,否则不会再次调用onCreateOptionsMenu。因此,我们只应该使用onCreateOptionsMenu来创建初始菜单状态,而不应使用它在Activity生命周期中对菜单执行任何更改。

    如果需要根据在Activity生命周期中发生的某些事件修改选项菜单,则应该通过onPrepareOptionsMenu方法实现。这个方法的参数中有一个Menu对象(即旧的Menu对象),我们可以使用它对菜单执行修改,如添加、移除、启用或禁用菜单项。(Fragment同样提供onPrepareOptionsMenu方法,只是不需要提供返回值)

    需要注意:在Android 3.0及更高版本中,当菜单项显示在应用栏中时,选项菜单被视为始终处于打开状态,说的就是常驻菜单。发生事件时,如果要执行菜单更新,则必须调用 invalidateOptionsMenu来请求系统调用onPrepareOptionsMenu方法

    对于上句话我的理解如下:

    1. 对于常驻菜单,如果你想动态修改菜单的话,就必须调用 invalidateOptionsMenu() 方法去更新,为什么呢? 因为调用 invalidateOptionsMenu方法后,会重新执行一遍 onCreateOptionsMenu 和 onPrepareOptionsMenu这两个方法。

    2. 对于溢出菜单,如果你想动态修改菜单的话,只需要在onPrepareOptionsMenu方法中实现即可,为什么呢?

        当你点击 更多(3个小点)图标的时候,就会回调onPrepareOptionsMenu方法

    4. 需求实现

            有了上面理论做支持,具体结合需求,网格和列表菜单属于常驻菜单,所以必须先要调用invalidateOptionsMenu()方法,然后在onPrepareOptionsMenu去写动态更新状态的代码

           图三中,关于属于溢出菜单,需求: 关于 菜单不能点击,所以也是在onPrepareOptionsMenu中去写动态更新状态的代码

    好了,我把代码展示出来:

    1. public class MainActivity extends AppCompatActivity {
    2. //默认为网格模式
    3. private boolean isShowGridModeIcon = true;
    4. //网格菜单 和 列表菜单
    5. private MenuItem gridMenuItem;
    6. private MenuItem listMenuItem;
    7. //用字符串来表示当前的文件列表显示模式
    8. private TextView viewModeStatus;
    9. @Override
    10. protected void onCreate(Bundle savedInstanceState) {
    11. super.onCreate(savedInstanceState);
    12. setContentView(R.layout.activity_main);
    13. Toolbar toolbar = findViewById(R.id.toolbar);
    14. setSupportActionBar(toolbar);
    15. viewModeStatus = findViewById(R.id.textview_refresh);
    16. }
    17. @Override
    18. public boolean onCreateOptionsMenu(Menu menu) {
    19. // Inflate the menu; this adds items to the action bar if it is present.
    20. getMenuInflater().inflate(R.menu.menu_main, menu);
    21. Log.e("test", "=====MenuTest onCreateOptionsMenu=====");
    22. gridMenuItem = menu.findItem(R.id.sub_menu_grid);
    23. listMenuItem = menu.findItem(R.id.sub_menu_list);
    24. return true;
    25. }
    26. @Override
    27. public boolean onPrepareOptionsMenu(Menu menu) {
    28. Log.e("test", "=====MenuTest onPrepareOptionsMenu=xxxx====");
    29. MenuItem aboutMenuItem = menu.findItem(R.id.action_about);
    30. //3个点)【更多】菜单中把 关于 设置为不可点击
    31. aboutMenuItem.setEnabled(false);
    32. if (isShowGridModeIcon) {
    33. gridMenuItem.setVisible(true);
    34. listMenuItem.setVisible(false);
    35. viewModeStatus.setText("当前为网格模式");
    36. } else {
    37. gridMenuItem.setVisible(false);
    38. listMenuItem.setVisible(true);
    39. viewModeStatus.setText("当前为列表模式");
    40. }
    41. return super.onPrepareOptionsMenu(menu);
    42. }
    43. @Override
    44. public boolean onOptionsItemSelected(MenuItem item) {
    45. // Handle action bar item clicks here. The action bar will
    46. // automatically handle clicks on the Home/Up button, so long
    47. // as you specify a parent activity in AndroidManifest.xml.
    48. int id = item.getItemId();
    49. Log.e("test", "=====MenuTest onOptionsItemSelected= sssss ====");
    50. //noinspection SimplifiableIfStatement
    51. if (id == R.id.action_settings) {
    52. return true;
    53. } else if (id == R.id.sub_menu_grid) {
    54. /*1. 点击网格图标,界面中文件布局显示变成网格模式*/
    55. //伪代码:setViewMode(State.MODE_GRID);
    56. isShowGridModeIcon = false;
    57. /*2. 图标变成切换list图标*/
    58. invalidateOptionsMenu();
    59. } else if (id == R.id.sub_menu_list) {
    60. /*1.点击列表图标,界面中文件布局显示变成列表模式*/
    61. //伪代码:setViewMode(State.MODE_LIST);
    62. isShowGridModeIcon = true;
    63. /*2. 图标变成切换grid图标*/
    64. invalidateOptionsMenu();
    65. }
    66. return super.onOptionsItemSelected(item);
    67. }
    68. }

    代码中加了打印log
    1.  在桌面上启动apk的时候,打印log如下:

    1. 21900 21900 E test : =====MenuTest onCreateOptionsMenu=====
    2. 21900 21900 E test : =====MenuTest onPrepareOptionsMenu=xxxx====

    2. 点击网格模式菜单时,打印log如下:

    1. 21900 21900 E test : =====MenuTest onOptionsItemSelected= sssss ====
    2. 21900 21900 E test : =====MenuTest onCreateOptionsMenu=====
    3. 21900 21900 E test : =====MenuTest onPrepareOptionsMenu=xxxx====

        如上分析,在onOptionsItemSelected方法中点击响应时,调用了invalidateOptionsMenu方法,所以会重新走一遍onCreateOptionsMenu, onPrepareOptionsMenu。

    3. 点击(3个点)更多菜单时,打印log如下:

    21900 21900 E test    : =====MenuTest onPrepareOptionsMenu=xxxx====
    

         如上分析, 对于溢出菜单类型,点击更多菜单时,只会回调onPrepareOptionsMenu方法

    5. 总结

            本篇文章讲解了动态菜单更新显示的过程,也是对menu菜单的一个小结,对于同类需求,可以仿照此demo,整个Demo代码我上传到这里:Android动态更新Menu菜单-Android文档类资源-CSDN下载

  • 相关阅读:
    将基站搬到天上,物联网迎来一场“升维”竞争
    利用Spring Boot框架做事件发布和监听
    .NET 7 的 AOT 到底能不能扛反编译?
    FRP内网穿透(待续)
    从源码入手探究一个因useImperativeHandle引起的Bug
    django--->自定义表名,建立索引
    为什么要使用MVP架构
    微软推送win11 22622.575补丁!
    音频3A算法详解
    测试阶段的最后两天,你会焦虑,不知所措吗?
  • 原文地址:https://blog.csdn.net/u012514113/article/details/126665826