菜单知识点总结(kotlin)

菜单的分类

菜单是Android应用中重要且常见的组成部分,从Android3.0开始,android从一个专用的“菜单按钮”转变为提供一个应用栏来呈现常见的用户操作。

主要可以分为三类:选项菜单上下文菜单/上下文操作模式以及弹出菜单。它们的主要区别如下:

  • 选项菜单:Activity的主菜单项,用于放置对应用产生全局影响的操作,如搜索/设置

  • 上下文菜单: 用户长按某一元素时出现的浮动菜单。它提供的操作将影响所选内容,主要应用于列表中的每一项元素(如长按列表项弹出删除对话框)。

  • 上下文操作模式: 在屏幕顶部栏(菜单栏)显示影响所选内容的操作选项,并允许用户选择多项,一般用于对列表类型的数据进行批量操作。

  • 弹出菜单: 以垂直列表形式显示一系列操作选项,一般由某一控件触发,弹出菜单将显示在对应控件的上方或下方。它适用于提供与特定内容相关的大量操作。

使用XML定义Menu

理论上而言,使用XML和Java代码都可以创建Menu。但是在实际开发中,往往通过XML文件定义Menu,这样做有以下几个好处:

  • XML可以可视化菜单结构

  • 菜单内容与应用的逻辑代码分离

  • 可以使用应用资源框架,为不同的平台版本、屏幕尺寸创建最合适的菜单(如对drawablestring等系统资源的使用)

  • 要定义Menu,我们需要在res文件夹下新建menu文件夹,用于存储与Menu相关的所有XML文件。
    该菜单文件的构成元素包括:

  • : 菜单根节点,能够包含一个或多个元素,是定义菜单项的容器。

  • : 菜单项节点,用于定义MenuItem,可以嵌套

    元素,以便创建子菜单。

  • : 元素的不可见容器(可选),可以使用它对菜单项进行分组,使一组菜单项共享可用性和可见性等属性。

XML菜单文件的示例代码如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/item_save"
        android:icon="@drawable/ic_settings"
        android:title="保存"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/item_settings"
        android:icon="@drawable/ic_settings"
        android:title="设置"/
</menu>

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

  • android:id:菜单项(MenuItem)的唯一标识
  • android:icon:菜单项的图标(可选)
  • android:title:菜单项的标题(必选)
  • android:showAsAction:指定菜单项的显示方式。常用的有ifRoomneveralwayswithText

showAsAction的取值说明

取值 说明
ifRoom 只有在标题栏中有空间时才将此项放置其中。如果没有足够的空间容纳标记为ifRoom的所有项,标题栏显示不了的菜单项将显示在溢出菜单中
withText 菜单项的文本和图标一起显示,可以将此值与其他值用竖线分隔共同起作用
never 菜单项永远不显示在标题栏,而隐藏在溢出菜单中
always 始终将此菜单项显示在标题栏。除非必须始终显示在标题栏,否则不要使用该值
collapseActionView 将菜单项折叠到一个按钮中,选择按钮展开,一般与ifRoom一起使用

选项菜单

普通选项菜单

要创建选项菜单,首先需要在XML文件中定义各个菜单项,具体代码如下:

XML代码

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/item_download"
        android:icon="@drawable/ic_downward"
        android:title="保存"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/item_settings"
        android:icon="@drawable/ic_settings"
        app:showAsAction="withText"
        android:title="设置"/>
</menu>

可以看到,我们在XML文件中定义了两个普通的菜单项。同时,每一个都有一个独特的showAsAction属性。

Activity通过重写onCreateOptionsMenu()方法加载创建的XML菜单资源,在此方法中使用MenuInflater类的inflate()方法将XML资源加载到Menu对象。除此之外,还可以使用add()添加菜单项来动态加载菜单项,具体实现如下:

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        //加载xml菜单资源
        var inflater:MenuInflater = menuInflater
        inflater.inflate(R.menu.option_menu,menu)
        //动态加载菜单项
        menu?.add(Menu.NONE,Menu.FIRST,3,"帮助")?.setIcon(R.drawable.ic_help)
        //设置菜单添加图标有效
        setIconsVisible(menu,true)
        return true
    }

当用户从选项菜单中选择菜单项时,系统将调用onOptionsItemSelected(),此方法将传递所选的MenuItem对象,通过调用该MenuItem对象的itemId方法获取菜单项的id,具体实现如下:

override fun onOptionsItemSelected(item: MenuItem): Boolean {
        //处理菜单项的选择
        return when(item.itemId){
            R.id.item_download-> {
                Toast.makeText(this,"下载",Toast.LENGTH_LONG).show()
                true
            }
            R.id.item_settings-> {
                Toast.makeText(this,"设置",Toast.LENGTH_LONG).show()
                true
            }
            Menu.FIRST -> {
                Toast.makeText(this,"帮助",Toast.LENGTH_LONG).show()
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

运行效果如图所示:
菜单知识点总结(kotlin)_第1张图片

默认情况下,即便在菜单XML文件中定义了android:icon属性,在溢出菜单中图标也不会显示,本案例使用反射的方法获取Menu对象的setOptionalIconsVisible(),调用该方法显示图标icon,具体实现如下:

//使用反射方法显示图标
    private fun setIconsVisible(menu: Menu?, flag:Boolean){
        //判断menu是否为空
        if(menu != null){
            try {
                val method:Method = menu.javaClass.getDeclaredMethod("setOptionalIconsVisible", TYPE)
                //强制使用方法
                method.isAccessible
                //调用该方法显示icon
                method.invoke(menu,flag)
            }catch (e:Exception){
                e.printStackTrace()
            }
        }
    }

然后将此方法添加到onCreateOptionsMenu()方法的return语句之前即可,具体实现如下:

  override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        //加载xml菜单资源
        var inflater:MenuInflater = menuInflater
        inflater.inflate(R.menu.option_menu,menu)
        //动态加载菜单项
        menu?.add(Menu.NONE,Menu.FIRST,3,"帮助")?.setIcon(R.drawable.ic_help)
        //设置菜单添加图标有效
        setIconsVisible(menu,true)

        return true
    }

上下文菜单及上下文操作模式

上下文菜单

通常上下文菜单是以浮动菜单的形式呈现的,用户长按(按住)一个支持上下文菜单的View时,菜单将以浮动列表的形式出现(类似于对话框)。 通常用户一次可对一个项目执行上下文操作(比如一个单独的控件或列表中的一项)。
Android提供了两种上下文操作菜单的方法。

  • 使用浮动上下文菜单:当长按某个View时,显示为菜单项的浮动列表。
  • 使用上下文操作模式:在屏幕顶部栏显示上下文操作栏选项。
    菜单知识点总结(kotlin)_第2张图片

1.创建浮动上下文菜单操作步骤

(1)注册菜单

通过调用registerForContextMenu()注册与上下文菜单关联的View。如果将ListViewGridView作为参数传入,那么每个列表将会有相同的浮动上下文菜单。

registerForContextMenu(binding.etName)
(2)加载菜单资源

ActivityFragment中实现onCreateContextMenu(),动态加载Menu资源。当注册后的View收到长按事件时,系统将调用此方法。

override fun onCreateContextMenu(
        menu: ContextMenu?,
        v: View?,
        menuInfo: ContextMenu.ContextMenuInfo?
    ) {
        super.onCreateContextMenu(menu, v, menuInfo)
        if (v?.id == R.id.et_name){
            //加载菜单XML资源
            val inflater:MenuInflater = menuInflater
            inflater.inflate(R.menu.context_menu,menu)
            //动态创建菜单项
//            menu?.add(Menu.NONE,Menu.FIRST,0,"拷贝")
//            menu?.add(Menu.NONE,Menu.FIRST+1,0,"粘贴")
//            menu?.add(Menu.NONE,Menu.FIRST+2,0,"清空")
        }
    }
(3)处理菜单项单击事件

ActivityFragment中实现onContextItemSelected(),实现菜单项的单击逻辑。

override fun onContextItemSelected(item: MenuItem): Boolean {
        return when(item.itemId){
            R.id.item_copy-> {
                Toast.makeText(this,item.title,Toast.LENGTH_LONG).show()
                true
            }
            R.id.item_paste-> {
                Toast.makeText(this,item.title,Toast.LENGTH_LONG).show()
                true
            }
            R.id.item_clear -> {
                binding.etName.setText("")
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }

上下文操作模式

上下文操作模式是ActionMode对象的系统实现,当用户长按控件或选中复选框等组件时调用此模式,会在屏幕顶部出现上下文操作栏,显示用户对所选控件执行的操作。

创建上下文操作栏模式的上下文菜单操作步骤

(1)实现ActionMode.Callback接口

在该接口的回调方法中,为上下文操作栏指定操作、响应菜单项的单击事件等。

// 上下文操作模式对象
    private var actionMode: ActionMode? = null

    // 实现ActionMode 中 CallBack回调接口
    val actionModeCallback: ActionMode.Callback = object : ActionMode.Callback {
        // 创建方法,在启动上下文操作模式startActionMode(Callback)时调用
        // 在此配置上下文菜单的资源
        override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
            mode.menuInflater.inflate(R.menu.context_menu, menu)
            return true
        }

        // 在创建方法后进行调用
        override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
            return false
        }

        // 菜单项被点击,类似onContextItemSelected()方法
        override fun onActionItemClicked(mode: ActionMode?, item: MenuItem): Boolean {
            when (item.itemId) {
                R.id.item_copy -> Toast.makeText(this@MainActivity, "拷贝", Toast.LENGTH_SHORT).show()
                R.id.item_paste -> Toast.makeText(this@MainActivity, "粘贴", Toast.LENGTH_SHORT).show()
                R.id.item_clear -> binding.etName.setText("")
                else -> {
                }
            }
            return true
        }

        // 上下文操作模式结束时被调用
        override fun onDestroyActionMode(mode: ActionMode?) {
            actionMode = null
        }
    }

该接口的回调方法与选项菜单的回调方法基本相同,只是需要传递与事件相关联的ActionMode对象。onActionItemClicked()方法用于处理菜单项的单击事件,与onContextItemSelected()类似。当系统销毁操作模式时,需要在onDestroyActionMode()方法中将actionMode变量设置为null。

(2)启动上下文操作模式

在用户名的EditView控件的长按事件中调用startActionMode()启动上下文操作模式,具体实现如下:

binding.etName.setOnLongClickListener(View.OnLongClickListener {
            if (actionMode!=null){
                return@OnLongClickListener false
            }
            actionMode = startActionMode(actionModeCallback)
            view.isSelected
            return@OnLongClickListener false
        })

调用startActionMode()方法返回ActionMode对象,通过该对象响应上下文操作栏的事件。

弹出菜单

PopupMenu是依赖View存在的模态菜单。如果空间足够,它将显示在相应View的下方,否则显示在其上方。
菜单知识点总结(kotlin)_第3张图片
弹出菜单适用于:

  • 为特定内容密切相关的操作提供溢出式菜单;
  • 提供类似Spinner控件,但不保留永久选择的下拉菜单。

弹出菜单操作步骤:

(1)创建菜单资源文件

res/menu目录下创建PopupMenuXML资源文件。

(2)创建并加载弹出菜单

EditText控件的单击事件方法中实例化PopupMenu对象,传递当前上下文对象及绑定的View对象,然后调用MenuInflater.inflate()方法加载菜单XML文件,调用PopupMenusetOnMenuItemClickListener()设置单击菜单项的事件监听器,最后调用show()方法显示菜单。

binding.etPhone.setOnClickListener(View.OnClickListener {
            //创建弹出菜单对象(最低版本11),第二个参数是绑定的那个view
            val popup:PopupMenu = PopupMenu(this,it)
            //获取菜单填充器
            val inflater:MenuInflater = popup.menuInflater
            //填充菜单
            inflater.inflate(R.menu.popup_menu,popup.menu)
            //绑定菜单项的单击事件
            popup.setOnMenuItemClickListener(this)
            //显示弹出菜单
            popup.show()
        })

(3)处理菜单项的OnMenuItemClick事件

MainActivity类实现PopupMenu.OnMenuItemClickListener接口,重写onMenuItemClick()回调方法,当用户选择菜单项时,系统调用onMenuItemClick()进行处理。

 // PopupMenu菜单项的点击事件
    override fun onMenuItemClick(item: MenuItem?): Boolean {
        when(item?.itemId){
            R.id.item_copy -> Toast.makeText(this,"复制...",Toast.LENGTH_SHORT).show()
            R.id.item_paste -> Toast.makeText(this,"粘贴...",Toast.LENGTH_SHORT).show()
            else -> {

            }
        }
        return false
    }

你可能感兴趣的:(学习,android,kotlin)