Android的Menu菜单,这一篇就够了

一直想抽时间把些较为基础的控件统一系统化抽取出来形成Demo,方便记录以及解答,以下是项目的效果,如果有欠缺的,欢迎小伙伴评论区留言,项目的GitHub地址:AndroidMenuDemo

效果

Menu的分类

菜单是Android应用中非常重要且常见的组成部分,主要分为三类:选项菜单上下文菜单弹出菜单

使用XML定义菜单

对于所有菜单类型,Android提供了标准的XML格式来定义菜单项。定义菜单项方法可以在XML菜单资源中定义菜单及其所有项,也可通过代码方式进行构建,推荐前者。定义后,可以在Activity或Fragment中扩充菜单资源(将其作为Menu对象加载)。

使用菜单资源是一种很好的做法,原因如下:

  • 更易于使用XML可视化菜单结构
  • 将菜单内容与应用的行为代码分离
  • 允许利用应用资源框架,为不同的平台版本、屏幕尺寸和其他配置创建备用菜单配置。

要定义菜单,需在项目res/menu/目录内创建一个XML文件,并使用以下元素构建菜单:

<menu>标签

定义Menu,即菜单项的容器。<menu>元素必须是该文件的根节点,并且能够包含一个或多个<item><group>元素。

<item>标签

<item>是菜单项,用于创建MenuItem,可能包含嵌套的<menu>元素,以便创建子菜单。常见属性如下:

  • android:id:菜单项(MenuItem)的唯一标识。
  • android:icon:菜单项的图标(可选),在溢出菜单中需要显示图标以及标题,需要额外代码中配置。
  • android:title:菜单项的标题(必选)
  • android:titleCondensed:菜单项的短标题(可选),当菜单项标题太长时会显示该属性值
  • android:onClick:方法名称。单击此菜单项时要调用的方法。该方法必须在Activity中声明为public,并将menuItem作为唯一参数,该参数指示单击的项。此方法优先于OnOptionsItemSelected() 的标准回调。
public void onGroupItemClick(MenuItem item) {}

警告:如果混淆代码时,请确保在混淆规则中对此属性方法进行排除,因为可能会破坏其功能。

  • android:showAsAction:指定菜单项的显示方式,多个属性值之间可以使用 | 隔开,参数值有:
有效值 描述
ifRoom 在空间足够时,菜单项会显示在菜单栏中,否则收纳入溢出菜单中。
always 菜单项永远不会被收纳到溢出菜单中,因此在菜单项过多的情况下可能超出菜单栏的显示范围。
never 菜单项永远只会出现在溢出菜单中。
withText 无论菜单项是否定义了icon属性,都只会显示它的标题,而不会显示图标。使用这种方式的菜单项默认会被收纳入溢出菜单中。
collapseActionView 此选项是在Api14引入的属性,搭配android:actionLayout或者android:actionViewClass使用,可起到折叠视图的效果
  • android:actionLayout:布局资源,动作视图使用的布局文件。
  • android:actionViewClass:类名,所使用的动作视图的全类名名称。例如当你使用SearchView只需要引入android.widget.SearchView。在Api 11引入的。
    警告:如果项目混淆记得添加对应的忽略文件。
  • android:actionProviderClass:操作提供器类名,例如使用ShareActionProvider需要引入"android.widget.ShareActionProvider",当然也可以自行自定义ActionProvider的子类,此选项在Api14引入
    警告:如果项目混淆记得添加对应的忽略文件。
  • android:numericShortcut:数字快捷键。
  • android:alphabeticShortcut:字母快捷键。
  • android:alphabeticModifiers:字母快捷键的修饰符,默认值为Control,有效值:
描述
META 对应Meta键
CTRL 对应Control键
ALT 对应Alt键
SHIFT 对应Shift键
SYM 对应Sym键
FUNTION 对应Function键

注意: 可以在属性中指定多个关键字。例如,android:alphabeticModifiers="CTRL|SHIFT",表示要触发相应的菜单项,用户需要同时按下两个Control和Shift键以及快捷键。

  • android:numericModifiers:数字快捷键的修饰符,用法同上。
  • android:checkable:是否可选中
  • android:checked:是否选中
  • android:visible:是否可见
  • android:enabled:是否启用
  • android:menuCategory:定义组优先级,常见值如下:
描述
container 对于属于容器的项目
system 对于系统提供的项目。
secondary 对于用户提供的辅助(不经常使用)选项的项目。
alternative 对于对当前显示的数据执行备用操作的项目。
  • orderInCategory:组内的重要性顺序

<group>标签

<group><item>元素的不可见容器(可选)。可以使用它对菜单项进行分组,使一组菜单项共享可用性和可见性等属性。常见属性不过多见解,可参考<item>标签

  • android:checkableBehavior:为组内项目实现单选或多选的选择模式。有三种可选值。
描述
none 所有项目均无法选中,默认值
single 组中只有一个项目可以选中(单选按钮)
all 所有项目均可选中(复选框)

XML方面需要注意点:当使用appcompat library时,菜单资源应引用app:namespace方式(showAsAction、actionViewClass、actionProviderClass),而不是android:namespace方式,对应引入的资源也需要调用兼容类;而相应如果不使用appcompat library,即需要使用android:namespace方式调用,这块涉及到Android Menu兼容配置。

选项菜单

选项菜单(OptionMenu):是应用的主菜单项,用于放置对应用起全局影响的操作,如搜索/设置等操作按钮。

选项菜单一般需要使用到以下几个方法:

  • public boolean onCreateOptionsMenu(Menu menu): 初始化Activity选项菜单方法,将要设置的菜单关联到menu中。在Fragment中调用public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

  • public boolean onOptionsItemSelected(MenuItem item): 选项菜单点击事件的处理方法。

  • public void onOptionsMenuClosed(Menu menu): 当选项菜单关闭时(用户使用后退/菜单按钮取消菜单),都会调用此方法。

  • public boolean onPrepareOptionsMenu(Menu menu):选项菜单显示前调用的方法,一般在此方法调整菜单,在高版本显示icon以及一些逻辑处理。

  • public boolean onMenuOpened(int featureId, Menu menu): 当用户打开溢出菜单选项时调用,当菜单从一种类型更改为另一种类型(例如,从图标菜单更改为拓展菜单),也调用此方法。一般用此方法监听拓展菜单是否被打开,用于重置一些菜单操作。此方法仅Activity存在。‘

    图1

    图2

XML实现方式

采用XML是实现菜单主要方式,官方也推荐此种方案,具体如下:

  • 实现option_menu.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/menu_search"
        android:orderInCategory="1"
        android:title="搜索菜单"
        android:icon="@android:drawable/ic_menu_search"
        app:showAsAction="always|collapseActionView"
        app:actionViewClass="android.support.v7.widget.SearchView"/>
    <item android:id="@+id/menu_share"
        android:title="分享菜单"
        android:icon="@android:drawable/ic_menu_share"
        android:orderInCategory="1"
        app:showAsAction="never"
        app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/>
    <item android:id="@+id/menu_collect"
        android:orderInCategory="1"
        android:title="收藏菜单"
        android:icon="@android:drawable/btn_star_big_on"
        app:actionLayout="@layout/layout_collect"
        app:showAsAction="never|collapseActionView"/>
    <item android:id="@+id/menu_previous"
        android:title="这是上一步的菜单展示效果"
        android:titleCondensed="上一步"
        android:orderInCategory="1"
        app:showAsAction="always"
        android:onClick="onPreviousMenu"/>
    <item android:id="@+id/menu_next"
        android:title="这是下一步的菜单展示效果"
        android:titleCondensed="下一步"
        android:orderInCategory="1"
        app:showAsAction="always"
        android:onClick="onNextMenu"/>
    <item android:id="@+id/menu_single_check"
        android:title="单选按钮"
        android:orderInCategory="1"
        app:showAsAction="withText">
        <menu>
            <group android:id="@+id/group_single"
                android:checkableBehavior="single"
                android:menuCategory="system">
                <item android:id="@+id/single_menu_01"
                    android:title="单选按钮01"
                    android:checked="true"/>
                <item android:id="@+id/single_menu_02"
                    android:title="单选按钮02"/>
                <item android:id="@+id/single_menu_03"
                    android:title="单选按钮03"/>
            </group>
        </menu>
    </item>
    <item android:id="@+id/menu_all_check"
        android:title="多选按钮"
        android:orderInCategory="1"
        app:showAsAction="withText">
        <menu>
            <group android:id="@+id/group_all"
                android:checkableBehavior="all"
                android:menuCategory="system"
                android:enabled="true">
                <item
                    android:id="@+id/all_menu_01"
                    android:title="多选按钮01"
                    android:checked="true"/>
                <item
                    android:id="@+id/all_menu_02"
                    android:title="多选按钮02"/>
                <item
                    android:id="@+id/all_menu_03"
                    android:title="多选按钮03"/>
            </group>
        </menu>
    </item>
</menu>
  • 在Activity或Fragment的onCreateOptionsMenu关联Option_menu.xml文件。

Activity中:

//创建选项菜单
getMenuInflater().inflate(R.menu.option_menu,menu);

Fragment中:

//创建选项菜单
inflater.inflate(R.menu.option_menu,menu);
  • 要让Fragment中的菜单项显示出来,还需要在Fragment中调用setHasOptionsMenu(true)方法。传入true作为参数表明Fragment需要加载菜单项。建议在Fragment的onCreate方法中调用这个方法
    /**
     * 要让Fragment中的菜单项显示出来,还需要在Fragment中调用setHasOptionsMenu(true)方法。
     * 传入true作为参数表明Fragment需要加载菜单项。
     * 建议在Fragment的onCreate方法中调用这个方法
     * @param savedInstanceState
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

代码实现方式

代码方式也是在onCreateOptionsMenu挂载添加menu子项,主要核心代码

//添加普通菜单
public MenuItem add(int groupId, int itemId, int order, CharSequence title);
//添加子菜单
SubMenu addSubMenu(final int groupId, final int itemId, int order, final CharSequence title);

参数说明:

  • groupId为组id,一般在主菜单中都是相同的组id,如果有subMenu的组id可以设置不同;
  • itemId为菜单项的唯一标识,参考xml布局方式中的id,一般用于菜单点击事件的区分;
  • order为序号,主要为组内排列顺序,相当于xml中的orderInCategory属性。
  • title菜单标题。
//通知系统刷新Menu
invalidateOptionsMenu();

上下文菜单

上下文菜单

上下文菜单

上下文菜单: 是用户长按某一元素出现的浮动菜单。它提供的操作将影响所选内容,主要应用于列表中的每一项元素(如长按表项弹出删除对话框)。
上下文菜单在view实现方式(这里只贴出核心代码,具体可以在底部下载gitHub下载完整案例):

  • ListView或RecycleView上下文选择菜单(有两种方法实现),单个View相同
    1. 方法一:通过registerForContextMenu方法实现注册
    //在onCreate中对列表进行注册
    registerForContextMenu(recyclerView);
    //在onDestroy中对列表进行解绑
    unregisterForContextMenu(recyclerView);
    //重写方法onCreateContextMenu
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        if(menuInfo instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){
            RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) menuInfo;
            if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){
                menu.setHeaderTitle("点击:"+mAdapter.getItem(contextMenuInfo.getPostion()));
                getMenuInflater().inflate(R.menu.context_menu,menu);
            }
        }
        super.onCreateContextMenu(menu, v, menuInfo);
    }
    
    1. 方法二:列表对象对onCreateContextMenu实现
    recyclerView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
            @Override
            public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
                if(menuInfo instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){
                    RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) menuInfo;
                    if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){
                        menu.setHeaderTitle("点击:"+mAdapter.getItem(contextMenuInfo.getPostion()));
                        getMenuInflater().inflate(R.menu.context_menu,menu);
                    }
                }
            }
        });
    
    以上是列表对于上下文菜单两种实现方式,监听可用过onContextItemSelected方法进行监听:
    @Override
     public boolean onContextItemSelected(MenuItem item) {
         if(item.getMenuInfo() instanceof RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo){
             RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo contextMenuInfo = (RecyclerViewWithContextMenu.RecyclerViewContextMenuInfo) item.getMenuInfo();
             if(contextMenuInfo != null && contextMenuInfo.getPostion() >= 0){
                 switch (item.getItemId()){
                     case R.id.context_menu_add:
                         Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":添加菜单被点击" , Toast.LENGTH_SHORT).show();
                         return true;
                     case R.id.context_menu_del:
                         Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":删除菜单被点击" , Toast.LENGTH_SHORT).show();
                         return true;
                     case R.id.context_menu_save:
                         Toast.makeText(this,mAdapter.getItem(contextMenuInfo.getPostion())+":保存菜单被点击" , Toast.LENGTH_SHORT).show();
                         return true;
                 }
             }
         }
         return super.onContextItemSelected(item);
     }
    
    针对于RecycleView列表,因为Google只是更加倾向其布局的重用性,提倡视图自定义,所以这里需要自己实现对于ContextMenuInfo的实现,不然RecycleView无法实现上下文菜单。
    public class RecyclerViewWithContextMenu extends RecyclerView {
    
         private RecyclerViewContextMenuInfo mContextMenuInfo = new RecyclerViewContextMenuInfo();
    
         public RecyclerViewWithContextMenu(Context context) {
             super(context);
         }
    
         public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs) {
             super(context, attrs);
         }
    
         public RecyclerViewWithContextMenu(Context context, @Nullable AttributeSet attrs, int defStyle) {
             super(context, attrs, defStyle);
         }
    
         @Override
         public boolean showContextMenuForChild(View originalView) {
             getPositionByChild(originalView);
             return super.showContextMenuForChild(originalView);
         }
    
         @Override
         public boolean showContextMenuForChild(View originalView, float x, float y) {
             getPositionByChild(originalView);
             return super.showContextMenuForChild(originalView, x, y);
         }
    
         /**
          * 重写实现ContextMenuInfo返回,不然在onCreateContextMenu无法获取到menuInfo信息
          * @return
          */
         @Override
         protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
             return mContextMenuInfo;
         }
    
         /**
          * 记录当前RecycleView中Item上下文菜单的Postion
          * @param originalView
          */
         private void getPositionByChild(View originalView){
             LayoutManager layoutManager = getLayoutManager();
             if(layoutManager != null){
                 int position = layoutManager.getPosition(originalView);
                 mContextMenuInfo.setPostion(position);
             }
         }
    
         public class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo{
             private int mPostion = -1;
    
             public int getPostion() {
                 return mPostion;
             }
    
             public void setPostion(int mPostion) {
                 this.mPostion = mPostion;
             }
         }
     }
    

上下文选择模式

上下文操作模式: 将在屏幕顶部栏(菜单栏)显示影响所选内容的操作选项,并允许用户选择多项,一般用于对列表类型的数据进行批量操作。上下文选择模式针对不同的列表(ListView、RecycleView)或单视图实现方式有些区别,具体核心代码如下所示:

  • ListView实现上下文选择模式
    ListView上下文选择模式
//为Listview配置上下文操作模式
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
            @Override
            public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
                //当列表中的项目选中或取消勾选时,这个方法会被触发
                //可以在这个方法中做一些更新操作,比如更改上下文操作栏的标题
                //这里显示已选中的项目数
                mode.setTitle("已选中:"+listView.getCheckedItemCount()+"项");
            }

            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                mode.getMenuInflater().inflate(R.menu.context_menu,menu);
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                //可以对上下文操作栏做一些更新操作(会被ActionMode的invalidate方法触发)
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()){
                    case R.id.context_menu_add:
                        StringBuilder sb = new StringBuilder();
                        for (long id:listView.getCheckedItemIds()) {
                            sb.append(id);
                        }
                        Toast.makeText(ContextMenu2Activity.this, "点击了添加按钮"+sb.toString(), Toast.LENGTH_SHORT).show();
                        //关闭上下文操作栏
                        mode.finish();
                        return true;
                    case R.id.context_menu_del:
                        Toast.makeText(ContextMenu2Activity.this, "点击了删除按钮", Toast.LENGTH_SHORT).show();
                        //关闭上下文操作栏
                        mode.finish();
                        return true;
                    case R.id.context_menu_save:
                        Toast.makeText(ContextMenu2Activity.this, "点击了保存按钮", Toast.LENGTH_SHORT).show();
                        //关闭上下文操作栏
                        mode.finish();
                        return true;
                    default:
                        return false;
                }
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                //在上下文操作栏被移除时会触发,可以对Activity做一些必要的更新
                //默认情况下,此时所有的选中项将会被取消选中

            }
        });
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Toast.makeText(ContextMenu2Activity.this, "点击了菜单", Toast.LENGTH_SHORT).show();
            }
        });

  • RecycleView实现上下文选择模式,在RecycleView中官方并未对齐支持上下文菜单以及上下文操作模式,但官方针对这个情况,官方于19年3月提出RecyclerView Selection插件库,试图解决此问题。
    RecycleView上下文选择模式

要将RecyclerView Selection库添加到Android Studio项目,请implementation 在app 模块的build.gradle 文件中提及以下依赖项:

//recycleView
implementation 'com.android.support:recyclerview-v7:28.0.0'
//recyclerview-selection(如果采用androidx可采用对应目录下的版本)
implementation 'com.android.support:recyclerview-selection:28.0.0'

在Adapter中明确指定指出此适配器的每个项目将具有类型的唯一稳定标识符。

//在adapter构造函数中实现
//明确指出此适配器的每个项目将具有类型的唯一稳定标识符非常重要Long。setHasStableIds(true);
/**
 * 为了能够使用项目的位置作为其唯一标识符,需重写getItemId
 * @param position
 * @return
 */
@Override
public long getItemId(int position) {
    return position;
}

在ViewHolder中实现可以调用以唯一标识所选列表项的方法。

public ItemDetailsLookup.ItemDetails getItemDetails(){
    return new LongItemDetails(getAdapterPosition(),getItemId());
}

在onBindViewHolder()方法(如果采用BaseRecyclerViewAdapterHelper需要在convert和convertPayloads)实现调用此代码块。

if(mSelectionTracker != null){
  if(mSelectionTracker.isSelected(getItemId(helper.getLayoutPosition()))){
            helper.getConvertView().setBackgroundColor(Color.parseColor("#80deea"));
            if(helper.tv instanceof CheckedTextView){
                ((CheckedTextView)helper.tv).setChecked(true);
            }
    }else {
        helper.getConvertView().setBackgroundColor(Color.WHITE);
        if(helper.tv instanceof CheckedTextView){
            ((CheckedTextView)helper.tv).setChecked(false);
        }
    }
}

并且挂载选择跟踪器

public void setSelectionTracker(SelectionTracker mSelectionTracker) {
    this.mSelectionTracker = mSelectionTracker;
}

实现ItemDetailsLookup

这个类将为选择库提供有关与用户选择关联的项目的信息,该选择基于MotionEvent,所以我们必须映射到我们的ViewHolders,返回产生MotionEvent事件的item的信息

public class MyItemDetailsLookup extends ItemDetailsLookup<Long> {
    private RecyclerView recyclerView;

    public MyItemDetailsLookup(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }

    @Nullable
    @Override
    public ItemDetails<Long> getItemDetails(@NonNull MotionEvent motionEvent) {
        View childViewUnder = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
        if(childViewUnder != null){
            RecyclerView.ViewHolder childViewHolder = recyclerView.getChildViewHolder(childViewUnder);
            if(childViewHolder instanceof SelectionQuickAdapter.SelectionQickViewHolder){
                return ((SelectionQuickAdapter.SelectionQickViewHolder)childViewHolder).getItemDetails();
            }
        }
        return null;
    }
}

在activity中创建选择跟踪器以及创建选择观察器

//创建选择跟踪器
mSelectionTracker = new SelectionTracker.Builder(
        "mySelection",
        recyclerView,
        new StableIdKeyProvider(recyclerView),      //密钥提供者
        new MyItemDetailsLookup(recyclerView),
        StorageStrategy.createLongStorage())
        .withSelectionPredicate(SelectionPredicates.<Long>createSelectAnything())
        .build();
mAdapter.setSelectionTracker(mSelectionTracker);
//创建选择观察器
mSelectionTracker.addObserver(new SelectionTracker.SelectionObserver() {
    @Override
    public void onItemStateChanged(@NonNull Object key, boolean selected) {
        super.onItemStateChanged(key, selected);
        Log.i(TAG, "onItemStateChanged: "+key+" to "+selected);
        setSelectionTitle();
    }

    @Override
    public void onSelectionRefresh() {
        super.onSelectionRefresh();
    }

    @Override
    public void onSelectionChanged() {
        super.onSelectionChanged();
        Log.i(TAG, "onSelectionChanged: ");
        setSelectionTitle();
    }

    @Override
    public void onSelectionRestored() {
        super.onSelectionRestored();
    }
});

至此,RecycleView创建上下文模式核心代码已完成,详情可参考RecyclerView-Selection

  • 为单个View设置上下文选择模式
    单View上下文选择模式

为单个View设置上下文操作模式同样可以分为两步:

  1. 实现ActionMode.Callback接口。在这个接口的回调方法中,可以为上下文操作栏加载Menu资源,也可以响应操作项目的点击事件,还可以处理其他需要的操作。
  2. 当需要显示操作栏时(例如,用户长按视图),调用Activity的startActionMode方法,并传入前面创建的Callback对象作为参数。
btnContextMode.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if(actionMode == null){
                    actionMode = startSupportActionMode(callback);
                    v.setSelected(true);        //设置View为选中状态
                    return true;
                }
                return false;
            }
        });
private ActionMode.Callback callback = new ActionMode.Callback() {
    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        mode.getMenuInflater().inflate(R.menu.context_menu,menu);
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return false;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
        switch (menuItem.getItemId()){
            case R.id.context_menu_add:
                Toast.makeText(ContextMenu4Activity.this, "点击了添加按钮", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.context_menu_del:
                Toast.makeText(ContextMenu4Activity.this, "点击了删除按钮", Toast.LENGTH_SHORT).show();
                return true;
            case R.id.context_menu_save:
                Toast.makeText(ContextMenu4Activity.this, "点击了保存按钮", Toast.LENGTH_SHORT).show();
                return true;
            default:
                return false;
        }
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        actionMode = null;
    }
};

弹出菜单

弹出菜单

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

private void createPopupMenu(View view){
        PopupMenu popupMenu = new PopupMenu(this,view);
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH){
            //写法1:getMenuInflater().inflate(R.menu.context_menu,popupMenu.getMenu());
            popupMenu.getMenuInflater().inflate(R.menu.context_menu,popupMenu.getMenu());
        }else {
            //在 API 级别 14 及更高版本中,您可以将两行合并在一起,使用 PopupMenu.inflate() 扩充菜单。
            popupMenu.inflate(R.menu.context_menu);
        }

        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem menuItem) {
                switch (menuItem.getItemId()){
                    case R.id.context_menu_add:
                        Toast.makeText(PopupMenuActivity.this, "点击保存菜单", Toast.LENGTH_SHORT).show();
                        return true;
                    case R.id.context_menu_del:
                        Toast.makeText(PopupMenuActivity.this, "点击删除菜单", Toast.LENGTH_SHORT).show();
                        return true;
                    case R.id.context_menu_save:
                        Toast.makeText(PopupMenuActivity.this, "点击保存菜单", Toast.LENGTH_SHORT).show();
                        return true;
                    default:
                        return false;
                }
            }
        });
        popupMenu.show();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容