一、ContextMenu的使用方法以及与OptionMenu的区别


>> ContextMenu是android的context menu上下文菜单,选择某项VIEW后长按menu键,就会显示出来。比如EditeText就可以通过长按来弹出拥有“cut”,"copy","paste"等项的ContextMenu。

实现ContextMenu,一般要用到以下三个方法:

(1)registerForContextMenu(getExpandableListView()); 

(2)onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo);

(3)onContextItemSelected(MenuItem item);


>>ContextMenu和OptionsMenu相比主要有以下区别: 1,ContextMenu必须通过Activity的 registerForContextMenu(View)来进行注册,而OptionsMenu不用。 2,ContextMenu不支持icon,而OptionsMenu支持。 3,ContextMenu可以有头,可以通过setHeaderIcon,setHeaderTitle,setHeaderView来设置头,否则就没有头。 4,弹出的方式不一样。 5, Options Menu的拥有者是Activity,而上下文菜单的拥有者是Activity中的View 。每个Activity有且只有一个Options Menu,它为整个Activity服务。而一个Activity往往有多个View,并不是每个View都有上下文菜单,这就需要我们 调用 registerForContextMenu ( View  view) 来指定
注意事项:ContextMenu的任何项在被选中后,整个ContextMenu会自动被系统关闭掉,即使多选项也是这样。这样的话,多选项的行为就有些怪异。查看Android的源码后,发现这是个Android的bug,应该是google没考虑多选项的情况。

尽管上下文菜单的拥有者是View,生成上下文菜单却是通过Activity中的onCreateContextMenu(ContextMenu menu, View v,ContextMenu.ContextMenuInfo menuInfo)方法,该方法很像生成Options Menu的onCreateOptionsMenu(Menu menu)方法。两者的不同在于,onCreateOptionsMenu只在用户第一次按“Menu”键时被调用,而onCreateContextMenu会在用户每一次长按View时被调用,而且View必须已经注册了上下文菜单。

另一个值得注意的就是上图中的ContextMenuInfo,该类的对象被传入onCreateContextMenu(ContextMenu menu, View v,ContextMenu.ContextMenuInfo menuInfo)方法,那么它有什么用呢?有时候,视图元素需要向上下文菜单传递一些信息,比如该View对应DB记录的ID等,这就要使用ContextMenuInfo。需要传递额外信息的View需要重写getContextMenuInfo()方法,返回一个带有数据的ContextMenuInfo实现类对象。


二、菜单OptionsMenu,ContextMenu和PopupMenu


       

OptionsMenu

在介绍Toolbar的时候,已经介绍了OptionsMenu的用法,OptionsMenu称为选项菜单,它可以设置在Toolbar中,显示方式有两种:直接显示在Toolbar中,显示在overflow菜单中。显示在overflow菜单中的按钮,也可以通过手机的menu键来调出,如果当前Activity是隐藏状态栏的,就需要使用menu键了。需要注意的是,手机的menu键只能调出包含在overflow里的item。

这里介绍一个特殊的方法,onPrepareOptionsMenu()。我们知道,创建OptionsMenu调用的方法是onCreateOptionsMenu,但是这个方法只会执行一次。如果在运行过程中对OptionsMenu进行操作,比如改变ItemIcon等,这时候就要用到onPrepareOptionsMenu,这里方法中可以获取到menu对象,对菜单栏进行操作。

onPrepareOptionsMenu通过invalidateOptionsMenu()方法调用。另外,点击overflow按钮也会调用onPrepareOptionsMenu,因为点击overflow的时候,隐藏的menu item会显示出来,也就是改变了menu的样式,所以点击overflow实际上已经调用了invalidateOptionsMenu()方法。

ContextMenu

ContextMenu翻译为上下文菜单,与OptionsMenu不同的是,ContextMenu是被view对象持有的,而OptionsMenu则是被Activity或者Fragment对象持有。

ContextMenu通过长按View调出。下面是一个长按Button弹出ContextMenu的例子:

创建menu的layout文件。当然这一步也可以省略,在代码中CreateMenu的时候动态添加item是一样的效果。

   

使用registerForContextMenu方法,为目标View注册上下文菜单,这里的例子是一个Button:

registerForContextMenu(mContButton);

创建上下文菜单,并设置监听:

@Override public boolean onCreateOptionsMenu(Menu menu) {     MenuInflater inflater = getMenuInflater();     inflater.inflate(R.menu.toobar_menu,menu);     return super.onCreateOptionsMenu(menu); }@Override public boolean onContextItemSelected(MenuItem item) {     switch (item.getItemId()){         case R.id.share_item:             Toast.makeText(this,"context menu add",Toast.LENGTH_SHORT).show();     }     return super.onContextItemSelected(item); }

到这里一个简单的上下文菜单功能就完成了。但是,实际使用的时候并不是这么简单。比如Recyclerview是无法通过这种方式实现的。

Recyclerview的ContextMenu实现方式:

ContextMenu的创建是由长按事件引发的,所以View里也设置了一个onCreateContextMenuListener,Recyclerview可以通过它来实现ContextMenu。

使用onCreateContextMenuListener的方式也比较灵活,可以在RecyclerView.ViewHolder类中实现这个接口,对itemview设置onCreateContextMenuListener,也可以在adapter中的onBindViewHolder中设置。

下面看具体的例子,这是一个item为TextView的Recyclerview:

//Holder中对ItemView设置创建ContextMenu监听 private class RecycleHolder extends RecyclerView.ViewHolder{        public RecycleHolder(View itemView) {            super(itemView);            itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {                @Override                public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {                    MenuInflater inflater = getMenuInflater();                    inflater.inflate(R.menu.search_menu,menu);                }            });        }//对ContextMenu的item设置响应    @Override    public boolean onContextItemSelected(MenuItem item) {        switch (item.getItemId()){            case R.id.share_item:                Toast.makeText(this,"context menu add",Toast.LENGTH_SHORT).show();        }        return super.onContextItemSelected(item);    }

这样就完成了对Recyclerview的ContextMenu设置。如果我们还想要更高级的功能,比如对不同的Item弹出不同的Menu,要怎样实现?

上面提到了,ContextMenu的创建实际上是监听了长按事件,我们可以同时对Item设置LongClickListenerCreateContextMenuListener,在LongClickListener中获取当前item的position,然后传递给CreateContextMenuListener。注意,LongClickListener返回值应该设置为false,否则事件不会传递给CreateContextMenuListener

上面的代码稍作修改:

//Holder 中不再设置监听private class RecycleHolder extends RecyclerView.ViewHolder{        private TextView mTextView;        public RecycleHolder(View itemView) {        mTextView = (TextView) itemView;        }    }private class RecycleAdapter extends RecyclerView.Adapter    {        private id = -1;        @Override        public LandViewActivity.RecycleHolder onCreateViewHolder(ViewGroup parent, int viewType) {            View TitleItemView = LayoutInflater.from(LandViewActivity.this).inflate(R.layout.item,parent,false);            return new LandViewActivity.RecycleHolder(TitleItemView);        }        @Override        public void onBindViewHolder(LandViewActivity.RecycleHolder holder, final int position) {            holder.mTextView.setText(mItems.get(position)); //对Recyclerview中的item设置长按监听,获取到id,注意返回值为false            holder.mTextView.setOnLongClickListener(new View.OnLongClickListener() {                @Override                public boolean onLongClick(View v) {                    id = position;                    return false;                }            });//对Recyclerview中的item设置创建菜单监听,根据id的值绝对是否创建菜单            holder.mTextView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {                @Override                public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {                    MenuInflater inflater = getMenuInflater();                    if (id%2==0) inflater.inflate(R.menu.search_menu,menu);                }            });        }        @Override        public int getItemCount() {            return mItems.size();        }    }

PopupMenu

PopupMenu是对某个View设置的弹出菜单,默认弹出在View的下方,如果控件不够则弹出在上方,点击该View 的时候就会弹出PopupMenu。

PopupMenu的定义如下:

        mContButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {//创建PopupMenu,并绑定到mContButton                PopupMenu popupMenu = new PopupMenu(LandViewActivity.this,mContButton);                MenuInflater menuInflater = getMenuInflater();                menuInflater.inflate(R.menu.toobar_menu, popupMenu.getMenu());                //popupMenu.inflate(R.menu.toobar_menu);  API 14 可以采用这种方式                popupMenu.show();//设置item的点击事件                popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {                    @Override                    public boolean onMenuItemClick(MenuItem item) {                        switch (item.getItemId()){                            case R.id.add_item:                                Toast.makeText(LandViewActivity.this,"Add button clicked",Toast.LENGTH_SHORT).show();                                break;                            case R.id.remo_item:                                invalidateOptionsMenu();                                Toast.makeText(LandViewActivity.this,"Remove button clicked",Toast.LENGTH_SHORT).show();                                break;                            case R.id.more_item:                                Toast.makeText(LandViewActivity.this,"More button clicked",Toast.LENGTH_SHORT).show();                                break;                            default:                                break;                        }                        return  true;                    }                });            }        });

PopupMenu的使用很简单简单,但是样式修改比较麻烦,在menu文件里修改是不起作用的,只能通过theme的Style来修改:

在当前theme下添加以下Item:    @style/MyListViewStyle    @style/PopupMenuText    @style/PopupMenuText对应的Style:        

三、上下文菜单(ContextMenu)使用案例

在Android中长按住一个控件(像一个文本显示框TextView,一个按钮Button都是一个控件)弹出的菜单为上下文菜单,创建一个上下文菜单分为下面几个步骤:
1、覆写onCreateContextMenu方法,生成对应的菜单子选项。
2、覆写onContextItemSelected方法,生成对应子选项的响应事件。
3、为一个控件注册上下文菜单。
下面看一个小例子:
1、在MainActivity的xml文件中添加一个TextView控件,ID为myTextV:

id="@+id/myTextV"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/hello_world" />

2、在MainActivity编写相应代码:

package com.yangzi.contextmenu;import android.os.Bundle;import android.app.Activity;import android.view.ContextMenu;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.ContextMenu.ContextMenuInfo;import android.widget.TextView;public class MainActivity extends Activity {    // 声明菜单子选项ID号    private static final int ITEM1 = Menu.FIRST;    private static final int ITEM2 = ITEM1 + 1;    private static final int ITEM3 = ITEM1+2;    //声明TextView控件    private TextView myTextV;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //找到文本显示控件        myTextV = (TextView)findViewById(R.id.myTextV);        //为该文本控件注册上下文菜单        registerForContextMenu(myTextV);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        // Inflate the menu; this adds items to the action bar if it is present.        getMenuInflater().inflate(R.menu.main, menu);        return true;    }    /*     * 覆写onCreateContextMenu方法,建立上下午菜单     *      * */    @Override    public void onCreateContextMenu(ContextMenu menu, View v,            ContextMenuInfo menuInfo) {        /*         * add方法的参数说明:          * 参数一:group ID(int) :组ID,用于标示该子选项属于哪一个组         * 参数二:item ID(int) :子选项ID,用于标示该子选项         * 参数三:order ID(int): 显示顺序,用于标示该子选项显示顺序,默认为0,表示按添加顺序显示         * 参数四:title (String): 用于定义该子选项的显示文字         *          * */        menu.add(0,ITEM1,0,"背景红色");        menu.add(0,ITEM2,0,"背景黄色");        menu.add(0,ITEM3,0,"背景蓝色");    }    /*     * 覆写该方法,添加相应的菜单相应事件     *      * */    @Override    public boolean onContextItemSelected(MenuItem item) {        /*         * 根据选择的子选项(通过item ID来区分),进行不同的响应         *          * 这里使用的颜色资源要在res/values文件夹内的color.xml文件中声明         *          * */        switch (item.getItemId()) {        case ITEM1:            //设置文本显示控件的背景            myTextV.setBackgroundColor(getResources().getColor(R.color.red));            break;        case ITEM2:            myTextV.setBackgroundColor(getResources().getColor(R.color.yellow));            break;        case ITEM3:            myTextV.setBackgroundColor(getResources().getColor(R.color.white));            break;        default:            break;        }        return true;    }}

更多相关文章

  1. Android画画板的制作方法
  2. [置顶] android6.0源码分析之Activity启动过程
  3. [置顶] Google Maps Android(安卓)API V2在Android(安卓)SDK lev
  4. Android属性动画源码分析(二)
  5. Android(安卓)模拟系统事件(一)
  6. Android(安卓)Studio API23之后使用HttpCLient包
  7. Eclipse build Android时不生成apk问题解决方法(加了JAR以后,不重
  8. ActionBar隐藏方法
  9. Android在程序中捕捉HOME键的方法

随机推荐

  1. Android屏幕方向及键盘状态
  2. HTTP 工具类 封装 For android
  3. android技术博客汇总
  4. android 写入data/data/包名/file/中
  5. MySQL对window函数执行sum函数可能出现的
  6. MySQL如何使用授权命令grant
  7. 一篇文章掌握MySQL的索引查询优化技巧
  8. 浅析mysql 定时备份任务
  9. 删除mysql服务的具体方法
  10. mysql数据存放的位置在哪