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);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章