關(guān)鍵字: ContextMenu
背景
我們經(jīng)常在列表的頁面中,點(diǎn)擊列表中的行芝加,一般進(jìn)入詳情頁面,長按列表中一行射窒,會彈出一個菜單藏杖,包含了對某一行的操作(編輯、刪除等等)脉顿,也知道通常的用法:
- 0x01. 在Activity中注冊需要上下文菜單的View:
registerForContextMenu(mListView);
- 0x02. 然后在Activity中繼承onCreateContextMenu方法蝌麸,添加菜單項(xiàng):
@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:每次長按出現(xiàn)上下文菜單都會調(diào)用這個方法**
/** * 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. 接下來長按列表中一行的時(shí)候,會彈出上下文菜單:

- 0x04. 點(diǎn)擊菜單后艾疟,在Activity中繼承onContextItemSelected方法進(jìn)行處理:
@Override
public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()){
}
}
- 0x05. 獲取Item標(biāo)識(id)
我們刪除數(shù)據(jù)庫或者一行記錄的時(shí)候来吩,要知道主鍵(一般是id)才能進(jìn)行操作,很多人就想辦法蔽莱,有的是把ListView的每個ItemView添加一個LongClickListener弟疆,然后長按的時(shí)候記錄下Position,然后在進(jìn)行相應(yīng)處理盗冷。
其實(shí)有更優(yōu)雅的做法怠苔,onContextItemSelected(MenuItem item)回調(diào)的參數(shù)item可以獲取item.getMenuInfo(),在ListView和Adapter的模式中仪糖,可以強(qiáng)制轉(zhuǎn)換成AdapterContextMenuInfo嘀略,拿到targetView(即所長按行的ItemVew恤溶,如果我們需要什么參數(shù),直接放到View.setTag中去即可):
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
int index = info.position;
View view = info.targetView;
至此帜羊,常見的用法就完了咒程,那么遇到其他自定義View呢?
- 0x06. 自定義View的ContextMenu實(shí)現(xiàn)
下面以用到的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饥瓷,每次彈出上下文菜單都會調(diào)用此方法,需要在這里更新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
時(shí)間: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.設(shè)置ItemView的LongClickable為true呢铆,不然不會出現(xiàn)上下文菜單(具體原因見ContextMenu原理分析);
holder.itemView.setLongClickable(true);