ContextMenu高級用法

關(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í)候,會彈出上下文菜單:
![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. 點(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);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹲缠,一起剝皮案震驚了整個濱河市棺克,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌线定,老刑警劉巖娜谊,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異斤讥,居然都是意外死亡纱皆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進(jìn)店門芭商,熙熙樓的掌柜王于貴愁眉苦臉地迎上來派草,“玉大人,你說我怎么就攤上這事铛楣“木欤” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵蛉艾,是天一觀的道長钳踊。 經(jīng)常有香客問我,道長勿侯,這世上最難降的妖魔是什么拓瞪? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮助琐,結(jié)果婚禮上祭埂,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好蛆橡,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布舌界。 她就那樣靜靜地躺著,像睡著了一般泰演。 火紅的嫁衣襯著肌膚如雪呻拌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天睦焕,我揣著相機(jī)與錄音藐握,去河邊找鬼。 笑死垃喊,一個胖子當(dāng)著我的面吹牛猾普,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播本谜,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼初家,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乌助?” 一聲冷哼從身側(cè)響起溜在,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎眷茁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纵诞,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡上祈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浙芙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片登刺。...
    茶點(diǎn)故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嗡呼,靈堂內(nèi)的尸體忽然破棺而出纸俭,到底是詐尸還是另有隱情,我是刑警寧澤南窗,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布揍很,位于F島的核電站,受9級特大地震影響万伤,放射性物質(zhì)發(fā)生泄漏窒悔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一敌买、第九天 我趴在偏房一處隱蔽的房頂上張望简珠。 院中可真熱鬧,春花似錦虹钮、人聲如沸聋庵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祭玉。三九已至氧映,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間攘宙,已是汗流浹背屯耸。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹭劈,地道東北人疗绣。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像铺韧,于是被迫代替她去往敵國和親多矮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Menus - 菜單 Menus display a list of choices on a transient...
    兩個朋友指甲閱讀 930評論 0 0
  • http://developer.android.youdaxue.com/guide/topics/ui/men...
    acc8226閱讀 1,194評論 0 3
  • afinalAfinal是一個android的ioc哈打,orm框架 https://github.com/yangf...
    passiontim閱讀 15,440評論 2 45
  • 將心情記錄成文字 讓文字作為心的釋放 好好的珍藏塔逃! 曾經(jīng)在微博上看到這樣一句話:“我讀過很多書,但后來大部分都忘記...
    向在亮閱讀 277評論 0 0
  • 11/21 劉宏波 安徽【每日一結(jié)構(gòu)】結(jié)構(gòu)思考力21天思維改善訓(xùn)練營 G:【自我探索】坦誠全視角料仗,刨根問底篩選湾盗,實(shí)...
    恒觀閱讀 217評論 0 0