上下文菜单与上下文操作模式

长按ListView或GridView列表项删除某一条记录是一种上下文操作,即它是与某个特定屏幕视图(单个列表项)而非整个屏幕相关联的。

在Honeycomb以前版本的设备上,上下文操作是在浮动上下文菜单中呈现的。而在之后的版本设备上,上下文操作主要是通过上下文操作栏呈现的。如下图:

上下文菜单与上下文操作模式_第1张图片

上下文菜单与上下文操作模式_第2张图片

对于上下文操作,我们必须实现两组不同的回调方法,一组用于上下文操作栏,一组用于浮动上下文菜单。

定义上下文菜单资源


<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_item_delete_crime"
        android:icon="@android:drawable/ic_delete"
        android:title="@string/delete_crime"/>

menu>

实施浮动上下文菜单

创建浮动上下文菜单

@Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        /**
         * 若定义了多个上下文菜单资源,通过检查传入onCreateContextMenu(...)方法的View视图ID,就可以自由决定
         * 使用那个资源来生成上下文菜单。
         */
        getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context,
                menu);
    }

为浮动上下文菜单登记视图

/**
         * ListFragment也有一个getListView()方法来获取ListFragment管理着的ListView。为什么不使用呢?
         * 这是因为,在onCreateView(...)方法完成调用并返回视图之前,getListView()方法返回的永远是null值。
         */
        ListView listView = (ListView) view.findViewById(android.R.id.list);
        // 为浮动上下文菜单登记视图,之后,长按视图则会触发上下文菜单的创建
        registerForContextMenu(listView);

响应浮动上下文菜单选择事件

@Override
    public boolean onContextItemSelected(MenuItem item) {
        // 因为ListView是AdapterView的子类,所以getMenuInfo()方法反悔了一个AdapterView.AdapterContextMenuInfo实例
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
                .getMenuInfo();
        int position = info.position;
        CrimeAdapter adapter = (CrimeAdapter) getListAdapter();
        Crime crime = adapter.getItem(position);
        switch (item.getItemId()) {
        case R.id.menu_item_delete_crime:
            CrimeLab.get(getActivity()).deleteCrime(crime);
            adapter.notifyDataSetChanged();
            return true;
        }
        return super.onContextItemSelected(item);
    }

定义上下文操作模式

创建上下文操作模式视图以及响应菜单选择事件

/**
         * ListFragment也有一个getListView()方法来获取ListFragment管理着的ListView。为什么不使用呢?
         * 这是因为,在onCreateView(...)方法完成调用并返回视图之前,getListView()方法返回的永远是null值。
         */
        ListView listView = (ListView) view.findViewById(android.R.id.list);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
            // 为浮动上下文菜单登记视图,之后,长按视图则会触发上下文菜单的创建
            registerForContextMenu(listView);
        } else {
            // 上下文操作模式下设置ListView的多选模式
            listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
            /**
             * 为listView设置一个实现AbsListView.MultiChoiceModeListener接口的监听器。该接口包含以下回调方法,视图在选中或撤销时会触发它:
             * public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked)
             * 
             * MultiChoiceModeListener实现了另一个接口,即ActionMode.CallBack。用户屏幕进入上下文操作时,会创建一个ActionMode类实例。
             * 随后在其生命周期内,ActionMode.CallBack接口的回调方法会在不同时点被调用。以下为ActionMode.CallBack接口必须实现的四个方法:
             */
            listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

                /**
                 * 在onCreateActionMode(ActionMode mode, Menu menu)方法之后,以及当前上下文操作栏需要刷新显示新数据时调用。
                 */
                @Override
                public boolean onPrepareActionMode(ActionMode arg0, Menu arg1) {
                    return false;
                }

                /**
                 * 在用户退出上下文操作模式或所选菜单项操作已被响应,从而导致ActionMode对象将要销毁时调用。默认的实现会导致已选视图被反选。
                 * 这里,也可完成在上下文操作模式下,响应菜单操作而引发的相迎fragment更新。
                 */
                @Override
                public void onDestroyActionMode(ActionMode arg0) {
                }

                /**
                 * 在ActionMode对象创建后调用。也是实例化上下文菜单资源,并显示在上下文操作栏上的任务完成的地方。
                 */
                @Override
                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                    /**
                     * 我们从操作模式,而非activity中获取MenuInflater的。操作模式负责对上下文操作栏进行配置。例如,
                     * 可调用ActionMode.setTitle(...)方法为上下文操作栏设置标题,而activity的MenuInflater则做不到这一点。
                     */
                    MenuInflater inflater = mode.getMenuInflater();
                    inflater.inflate(R.menu.crime_list_item_context, menu);
                    return true;//这里必须返回true,否则会导致操作模式创建失败。
                }

                /*
                 * 在用户选中某个菜单项操作时调用。是响应上下文菜单项操作的地方。
                 */
                @Override
                public boolean onActionItemClicked(ActionMode mode,
                        MenuItem item) {
                    switch (item.getItemId()) {
                    case R.id.menu_item_delete_crime:
                        CrimeAdapter adapter = (CrimeAdapter) getListAdapter();
                        CrimeLab crimeLab = CrimeLab.get(getActivity());
                        for (int i = adapter.getCount(); i >= 0; i--) {
                            if(getListView().isItemChecked(i)){
                                crimeLab.deleteCrime(adapter.getItem(i));
                            }
                        }
                        mode.finish();
                        adapter.notifyDataSetChanged();
                        return true;
                    default:
                        return false;
                    }
                }

                @Override
                public void onItemCheckedStateChanged(ActionMode mode,
                        int position, long id, boolean checked) {
                }
            });
        }

改变已激活视图的显示背景

视图处于激活状态,是指该视图已被用户标记为关注处理对象。基于视图的状态,可使用state list drawable 资源来改变其显示背景。state list drawable是一种以XML定义的资源。该资源定义中,我们指定一个drawable(位图或彩图),并列出该drawable对应的状态。state list drawable一般定义在res/drawable目录下。


<selector xmlns:android="http://schemas.android.com/apk/res/android">

    
    <item android:drawable="@android:color/darker_gray" 
          android:state_activated="true"/>

selector>

实现其他视图的上下文操作模式

如果视图即非ListView,也非GridView,要使用上下文操作模栏,该如何处理?

首先,设置一个实现View.onLongclickListener接口的监听器。然后再监听器实现体内,调用Activity.startActionMode(…)方法创建一个ActionMode实例。startActionMode(…)方法需要实现一个ActionMode.Callback接口的对象作为参数。

代码地址

你可能感兴趣的:(Android编程权威指南)