ConxtMenu高级用法

Posted lijianming180

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConxtMenu高级用法相关的知识,希望对你有一定的参考价值。

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

  • 0x01. 在Activity中注册需要上下文菜单的View:
    registerForContextMenu(mListView);
  • 0x02. 然后在Activity中继承onCreateContextMenu方法,添加菜单项:

    1
    2
    3
    4
    5
    6
    7
    8
    @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:每次长按出现上下文菜单都会调用这个方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /** * 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. 接下来长按列表中一行的时候,会弹出上下文菜单:
    技术图片

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

    1
    2
    3
    4
    5
    @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中去即可):

    1
    2
    3
    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());
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /** * 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;

1
2
3
4
/** * {@inheritDoc} */
public boolean showContextMenuForChild(View originalView) {
return mParent != null && mParent.showContextMenuForChild(originalView);
}

  • 0x07. 自定义RecycleView的ContextMenu全部代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
大专栏  ConxtMenu高级用法v class="line">163
164
165
166
167
168
169
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);

以上是关于ConxtMenu高级用法的主要内容,如果未能解决你的问题,请参考以下文章

前端框架怎么用??用的好处是什么?

c_cpp 加载源图像固定用法(代码片段,不全)

JavaScript笔试题(js高级代码片段)

Vue3官网-高级指南(十七)响应式计算`computed`和侦听`watchEffect`(onTrackonTriggeronInvalidate副作用的刷新时机`watch` pre)(代码片段

SQL Select 语句的用法

shell高级用法--函数