ContextMenu高级用法

51CT0不支持MarkDown,原文请至:简书

关键字: ContextMenu

##背景

我们经常在列表的页面中,点击列表中的行,一般进入详情页面,长按列表中一行,会弹出一个菜单,包含了对某一行的操作(编辑、删除等等),也知道通常的用法:

- 0x01. 在Activity中注册需要上下文菜单的View:

`registerForContextMenu(mListView);`

- 0x02. 然后在Activity中继承onCreateContextMenu方法,添加菜单项:

```

@Override

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {

Log.d(LOG_TAG, "onCreateContextMenu");

super.onCreateContextMenu(menu, v, menuInfo);

menu.setHeaderTitle(R.string.prompt);

menu.add(Menu.NONE, R.id.context_menu_item_delete_record, Menu.NONE, R.string.delete_record);//groupId, itemId, order, title

menu.add(Menu.NONE, R.id.context_menu_item_delete_record_with_file, Menu.NONE, R.string.delete_record_with_file);

}```

**PS:每次长按出现上下文菜单都会调用这个方法**

```

/** * Called when a context menu for the {@code view} is about to

be shown. * Unlike {@link #onCreateOptionsMenu(Menu)}, this will

be called every * time the context menu is about to be shown and

should be populated for * the view (or item inside the view for {@link

AdapterView} subclasses, * this can be found in the {@code

menuInfo})). * <p> * Use {@link #onContextItemSelected(android.view.MenuItem)} to know when an

* item has been selected. * <p> * It is not safe to hold onto the

context menu after this method returns. * */

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {

}

```

- 0x03. 接下来长按列表中一行的时候,会弹出上下文菜单:

![device-2015-11-04-141103.png](http://upload-images.jianshu.io/upload_images/728306-c1f997a517d009c7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 0x04. 点击菜单后,在Activity中继承onContextItemSelected方法进行处理:

```

@Override

public boolean onContextItemSelected(MenuItem item) {

switch (item.getItemId()){

}

}

```

- 0x05. 获取Item标识(id)

我们删除数据库或者一行记录的时候,要知道主键(一般是id)才能进行操作,很多人就想办法,有的是把ListView的每个ItemView添加一个LongClickListener,然后长按的时候记录下Position,然后在进行相应处理。

其实有更优雅的做法,onContextItemSelected(MenuItem item)回调的参数item可以获取item.getMenuInfo(),在ListView和Adapter的模式中,可以强制转换成AdapterContextMenuInfo,拿到targetView(即所长按行的ItemVew,如果我们需要什么参数,直接放到View.setTag中去即可):

```

AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();

int index = info.position;

View view = info.targetView;

```

至此,常见的用法就完了,那么遇到其他自定义View呢?

- 0x06. 自定义View的ContextMenu实现

下面以用到的RecycleView为例,没有了ListView及其Adapter的封装,我们需要自己处理ContextMenu。

最重要的是继承View的两个方法:

1.上下文菜单Item的附加信息(上面item.getMenuInfo());

```

/** * Views should implement this if they have extra information to

associate * with the context menu. The return result is supplied as a

parameter to * the {@link

OnCreateContextMenuListener#onCreateContextMenu(ContextMenu,

View, ContextMenuInfo)} * callback. * * @return Extra information

about the item for which the context menu *         should be shown.

This information will vary across different *         subclasses of View. */

protected ContextMenuInfo getContextMenuInfo() {

return null;

}

```

2.ViewGroup的showContextMenuForChild,每次弹出上下文菜单都会调用此方法,需要在这里更新ContextMenuInfo;

```

/** * {@inheritDoc} */

public boolean showContextMenuForChild(View originalView) {

return mParent != null && mParent.showContextMenuForChild(originalView);

}

```

- 0x07. 自定义RecycleView的ContextMenu全部代码

```

package com.lbrant.phone.view;

import android.content.Context;

import android.support.v7.widget.RecyclerView;

import android.util.AttributeSet;

import android.util.Log;

import android.view.ContextMenu;

import android.view.View;

/**

* 作者:dell

* 时间:2015/11/3 18:34

* 文件:PhoneRecorder

* 描述:

*/

public class ContextMenuRecyclerView extends RecyclerView {

private static final String LOG_TAG = "ContextMenuRecyclerView";

private RecyclerContextMenuInfo mContextMenuInfo = new RecyclerContextMenuInfo();

public ContextMenuRecyclerView(Context context) {

super(context);

}

public ContextMenuRecyclerView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public ContextMenuRecyclerView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

}

@Override

protected ContextMenu.ContextMenuInfo getContextMenuInfo() {

return mContextMenuInfo;

}

@Override

public boolean showContextMenuForChild(View originalView) {

Log.d(LOG_TAG, "showContextMenuForChild");

Object tag = originalView.getTag();

if (tag instanceof RecyclerItemMarker) {

mContextMenuInfo.mRecycleItemMarker = (RecyclerItemMarker) tag;

}

return super.showContextMenuForChild(originalView);

}

public static class RecyclerItemMarker {

public final int position;

public final Object obj;

public RecyclerItemMarker(int position, Object obj) {

this.position = position;

this.obj = obj;

}

}

public static class RecyclerContextMenuInfo implements ContextMenu.ContextMenuInfo {

public RecyclerItemMarker mRecycleItemMarker;

}

}

private class RecordRecycleViewAdapter extends RecyclerView.Adapter<RecordRecycleViewAdapter.RecordViewHolder> {

private Cursor mCallRecordCursor;

private int mIdIndex;

private int mPhoneNumberIndex;

private int mCallTimeIndex;

private int mDurationIndex;

private int mPathIndex;

public RecordRecycleViewAdapter(Cursor cursor) {

mCallRecordCursor = cursor;

updateCursorColumnIndex();

}

private void updateCursorColumnIndex() {

if (mCallRecordCursor != null) {

mIdIndex = mCallRecordCursor.getColumnIndex(BaseDatabaseHelper.RECORDS_COLUMNS._ID);

mPhoneNumberIndex = mCallRecordCursor.getColumnIndex(BaseDatabaseHelper.RECORDS_COLUMNS.NUMBER);

mCallTimeIndex = mCallRecordCursor.getColumnIndex(BaseDatabaseHelper.RECORDS_COLUMNS.CALL_TIME);

mDurationIndex = mCallRecordCursor.getColumnIndex(BaseDatabaseHelper.RECORDS_COLUMNS.DURATION);

mPathIndex = mCallRecordCursor.getColumnIndex(BaseDatabaseHelper.RECORDS_COLUMNS.PATH);

}

}

@Override

public RecordRecycleViewAdapter.RecordViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

View contentView = LayoutInflater.from(parent.getContext()).inflate(R.layout.record_list_item, parent, false);

RecordViewHolder viewHolder = new RecordViewHolder(contentView);

return viewHolder;

}

@Override

public void onBindViewHolder(RecordRecycleViewAdapter.RecordViewHolder holder, final int position) {

holder.itemView.setLongClickable(true);

if (mCallRecordCursor != null && mCallRecordCursor.moveToPosition(position)) {

long id = mCallRecordCursor.getLong(mIdIndex);

String phoneNumber = mCallRecordCursor.getString(mPhoneNumberIndex);

long seconds = mCallRecordCursor.getLong(mDurationIndex);

String callTime = mCallRecordCursor.getString(mCallTimeIndex);

String path = mCallRecordCursor.getString(mPathIndex);

String duration = String.format("%1$02d:%2$02d:%3$02d", seconds / 3600, seconds % 3600 / 60, seconds % 60);

RecordInfo info = new RecordInfo();

info.setId(id);

info.setPhoneNumber(phoneNumber);

info.setSecondsDuration(seconds);

info.setCallTime(callTime);

info.setPath(path);

holder.itemView.setTag(new ContextMenuRecyclerView.RecyclerItemMarker(position, info));

holder.mTextViewPhoneNumber.setText(phoneNumber);

holder.mTextViewDuration.setText(duration);

holder.mTextviewCallTime.setText(callTime);

Cursor cursor = queryContactByPhoneNumber(ContactsContract.CommonDataKinds.Phone.NUMBER + " = ‘" + phoneNumber + "‘");

if (cursor != null) {

if (cursor.moveToNext()) {

long contactId = cursor.getInt(0);

Cursor contactCursor = queryContact(ContactsContract.Contacts._ID + "=" + contactId);

if (contactCursor != null) {

holder.mTextViewName.setText(contactCursor.getString(1));

contactCursor.close();

}

}

cursor.close();

}

}

}

@Override

public void onViewRecycled(RecordViewHolder holder) {

super.onViewRecycled(holder);

holder.itemView.setOnCreateContextMenuListener(null);

}

@Override

public int getItemCount() {

return mCallRecordCursor == null ? 0 : mCallRecordCursor.getCount();

}

public void changeCursor(Cursor cursor) {

if (cursor != mCallRecordCursor) {

if (mCallRecordCursor != null) {

mCallRecordCursor.close();

}

mCallRecordCursor = cursor;

updateCursorColumnIndex();

notifyDataSetChanged();

}

}

public class RecordViewHolder extends RecyclerView.ViewHolder {

private ImageView mImageViewAvatar;

private TextView mTextViewPhoneNumber;

private TextView mTextViewName;

private TextView mTextviewCallTime;

private TextView mTextViewDuration;

public RecordViewHolder(View itemView) {

super(itemView);

mImageViewAvatar = (ImageView) itemView.findViewById(R.id.imageViewAvatar);

mTextViewName = (TextView) itemView.findViewById(R.id.textViewName);

mTextViewPhoneNumber = (TextView) itemView.findViewById(R.id.textViewPhoneNumber);

mTextviewCallTime = (TextView) itemView.findViewById(R.id.textViewCallTime);

mTextViewDuration = (TextView) itemView.findViewById(R.id.textViewDuration);

}

}

}

```

**有两个地方需要注意:**

1.onBindViewHolder中给ItemView添加Tag;

2.设置ItemView的LongClickable为true,不然不会出现上下文菜单(具体原因见ContextMenu原理分析);

holder.itemView.setLongClickable(true);

时间: 2024-09-30 04:22:44

ContextMenu高级用法的相关文章

ConxtMenu高级用法

##背景我们经常在列表的页面中,点击列表中的行,一般进入详情页面,长按列表中一行,会弹出一个菜单,包含了对某一行的操作(编辑.删除等等),也知道通常的用法: 0x01. 在Activity中注册需要上下文菜单的View: registerForContextMenu(mListView); 0x02. 然后在Activity中继承onCreateContextMenu方法,添加菜单项: 1 2 3 4 5 6 7 8 @Override public void onCreateContextMe

jquery on高级用法

on()的高级用法 针对自己处理机制中,不仅有on方法,还有根据on演变出来的live方法(1.7后去掉了),delegate方法等等.这些方法的底层实现部分 还是on方法,这是利用了on的另一个事件机制委托的机制衍变而来的 委托机制 .on( events ,[ selector ] ,[ data ], handler(eventObject) ) 在on的第二参数中提供了一个selector选择器,简单的来描述下 参考下面3层结构 <div class="left">

Newtonsoft.Json高级用法

手机端应用讲究速度快,体验好.刚好手头上的一个项目服务端接口有性能问题,需要进行优化.在接口多次修改中,实体添加了很多字段用于中间计算或者存储,然后最终用Newtonsoft.Json进行序列化返回数据,经过分析一个简单的列表接口每一行数据返回了16个字段,但是手机APP端只用到了其中7个字段,剩余9个字段的数据全部都是多余的,如果接口返回数据为40K大小,也就是说大约20K的数据为无效数据,3G网络下20K下载差不多需要1s,不返回无效数据至少可以节约1s的时间,大大提高用户体验.本篇将为大家

&lt;06&gt;变量使用前易犯错误总结+if语句介绍及基本格式+if-else语句及嵌套+if语句的高级用法+if语句的使用注意点+arc4random_uniform 函数导入一个头文件&lt;stdlib.h&gt;+

1)变量要初始化 -1 0  ,或者1 依据程序而定 ---------------------------------- if语句介绍及基本格式 分支语句结构 1)if 格式: if(表达式/常量/变量){ 语句块1; } 原理: 1)先计算小括号中的表达式的值 真(1) 会执行 大括号中的语句 假(0) 不会执行 大括号中的语句 2) switch 1 #include <stdio.h> 2 3 int main(int argc, const char * argv[]) { 4 5

JS Replace() 高级用法(转)

在很多项目中,我们经常需要使用JS,在页面前面对前台的某些元素做做修改,js 的replace()方法就必不可少. 经常使用"ABCABCabc".replace("A","B")的同学应该会比较清楚,改语句的最终结果是BBCABC,这种方法只能替换 第一个匹配的元素.如果替换所有呢?使用正则表达式即可: "ABCABCabc".replace(/A/g,"B") 即可. 那如果想替换A的同时也可以替换a呢

Python爬虫的Urllib库有哪些高级用法?

本文和大家分享的主要是python爬虫的Urllib库的高级用法相关内容,一起来看看吧,希望对大家学习python有所帮助. 1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它 是一段HTML代码,加 JS.CSS,如果把网页比作一个人,那么HTML便是他的骨架,JS便是他的肌肉,CSS便是它的衣服.所以最重要的部分是存在于HTML中的,下面我 们就写个例子来扒一个网页下来. imp

GUN sed高级用法,sed脚本编写

这里举一些sed常用的高级用法例子经供参考: 一下操作都针对file.txt文件作修改 [[email protected] ~]# cat file.txt libgcc-4.4.7-4.el6.x86_64 setup-2.8.14-20.el6_4.1.noarch tzdata-2013g-1.el6.noarch jakarta-commons-collections-3.2.1-3.4.el6.noarch filesystem-2.4.30-3.el6.x86_64 mesa-dr

Git log高级用法

格式化Log输出 首先,这篇文章会展示几种git log格式化输出的例子.大多数例子只是通过标记向git log请求或多或少的信息. 如果你不喜欢默认的git log格式,你可以用git config的别名功能来给你想要的格式创建一个快捷方式. Oneline --oneline标记把每一个提交压缩到了一行中.它默认只显示提交ID和提交信息的第一行.git log --oneline的输出一般是这样的: 0e25143 Merge branch 'feature' ad8621a Fix a b

JAVA正则表达式高级用法(分组与捕获)

正则表达式在字符串处理中经常使用,关于正则简单的用法相信有一点程序基础的人都懂得一些,这里就不介绍简单基础了.这里主要讲解一下在JAVA中实现了的正则的高级用法-分组与捕获.对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a.这些限定符如下所示: X ?     X ,一次或一次也没有X *     X ,零次或多次X +     X ,一次或多次X { n }     X ,恰好 n 次X { n ,}     X ,