第 18 章 上下文菜单与上下文操作模式

请参考教材,全面理解和完成本章节内容... ...

本章,我们将为应用实现长按列表项删除crime记录的功能。删除一条crime记录是一种上下文操作(contextual action),即它是与某个特定屏幕视图(单个列表项)而非整个屏幕相关联的。

在Honeycomb以前版本的设备上,上下文操作是在浮动上下文菜单中呈现的。而在之后版本的设备上,上下文操作主要是通过上下文操作栏呈现的。位于activity的操作栏之上,上下文操作栏为用户提供了各种操作,如图18-1所示。

图18-1 长按列表项删除一条crime记录

第16章我们已看到,对于选项菜单而言,处理不同API级别的兼容性问题很简单:只需定义一种菜单资源并实现一组菜单相关的回调方法,不同设备上的操作系统会自行决定菜单项的显示方式。

而对于上下文操作,事情就没那么简单了。虽然还是定义一种菜单资源,但我们必须实现两组不同的回调方法,一组用于上下文操作栏,一组用于浮动上下文菜单。

本章,我们将在运行API 11级及以上系统版本的设备上实施一个上下文操作,然后,再在Froyo及Gingerbread设备上实施一个浮动上下文菜单。

(我们也许见过旧设备上运行的带有上下文操作栏的应用。通常来说,这些应用都是基于一个名为ActionBarSherlock的第三方库开发的。该库通过模仿复制为旧系统设备实现了上下文操作栏的功能。本章末尾我们将详细讨论ActionBarSherlock库的使用。)

18.1 定义上下文菜单资源

在res/menu/目录中,以menu为根元素,新建名为crime_list_item_context.xml的菜单资源文件。然后参照代码清单18-1添加需要的菜单项。

代码清单18-1 用于crime列表的上文菜单(crime_list_item_context.xml)

以上定义的菜单资源将用于上下文操作栏和浮动上下文菜单的实施。

18.2 实施浮动上下文菜单

首先,我们来创建浮动上下文菜单。Fragment的回调方法类似于第16章中用于选项菜单的回调方法。要实例化生成一个上下文菜单,可使用以下方法:

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)

要响应用户的上下文菜单选择,可实现以下Fragment方法:

public boolean onContextItemSelected(MenuItem item)

18.2.1 创建上下文菜单

在CrimeListFragment.java中,实现onCreateContextMenu(...)方法,实例化菜单资源,并用它填充上下文菜单,如代码清单18-2所示。

代码清单18-2 创建上下文菜单(CrimeListFragment.java)

不像onCreateOptionsMenu(...)方法,以上菜单回调方法不接受MenuInflater实例参数,因此,我们应首先获得与CrimeListActivity关联的MenuInflater。然后调用MenuInflater.inflate(...)方法,传入菜单资源ID和ContextMenu实例,用菜单资源文件中定义的菜单项填充菜单实例,从而完成上下文菜单的创建。

当前我们只定义了一个上下文菜单资源,因此,无论用户长按的是哪个视图,菜单都是以该资源实例化生成的。假如定义了多个上下文菜单资源,通过检查传入onCreateContextMenu(...)方法的View视图ID,我们可以自由决定使用哪个资源来生成上下文菜单。

18.2.2 为上下文菜单登记视图

默认情况下,长按视图不会触发上下文菜单的创建。要触发菜单的创建,必须调用以下Fragment方法为浮动上下文菜单登记一个视图:

public void registerForContextMenu(View view)

该方法需传入触发上下文菜单的视图。

在CriminalIntent应用里,我们希望点击任意列表项,都能弹出上下文菜单。这岂不是意味着需要逐个登记列表项视图吗?不用那么麻烦,直接登记ListView视图即可,然后它会自动登记各个列表项视图。

CrimeListFragment.onCreateView(...)方法中,引用并登记ListView,如代码清单18-3所示。

代码清单18-3 为上下文菜单登记ListView(CrimeListFragment.java)

onCreateView(...)方法中,使用android.R.id.list资源ID获取ListFragment管理着的ListViewListFragment也有一个getListView()方法,但在onCreateView(...)方法中却无法使用。这是因为,在onCreateView()方法完成调用并返回视图之前,getListView()方法返回的永远是null值。

运行CriminalIntent应用,长按任意列表项,可弹出含“删除”菜单项的浮动菜单,如图18-2所示。

18-2 长按列表项弹出上下文菜单项

18.2.3 响应菜单项选择

Delete菜单项要可用,需要一个能够从模型层删除crime数据的方法。在CrimeLab.java中,新增deleteCrime(Crime)方法,如代码清单18-4所示。

代码清单18-4 新增删除crime的方法(CrimeLab.java)

然后,在onContextItemSelected(MenuItem)方法中处理菜单项选择事件。MenuItem有一个资源ID可用于识别选中的菜单项。除此之外,还需明确具体要删除的crime对象,才能确定用户想要删除crime数据的意图。

可调用MenuItemgetMenuInfo()方法,获取要删除的crime对象的信息。该方法返回一个实现了ContextMenu.ContextMenuInfo接口的类实例。

CrimeListFragment中,新增onContextItemSelected(MenuItem)实现方法,使用menu信息和adapter,确定被长按的Crime对象,然后从模型层数据中删除它,如代码清单18-5所示。(注:import android.widget.AdapterView.AdapterContextMenuInfo;

代码清单18-5 监听上下文菜单项选择事件(CrimeListFragment.java)

以上代码中,因为ListViewAdapterView的子类,所以getMenuInfo()方法返回了一个AdapterView.AdapterContextMenuInfo实例。然后,将getMenuInfo()方法的返 回结果进行类型转换,获取选中列表项在数据集中的位置信息。最后,使用列表项的位置,获取要删除的Crime对象。

运行CriminalIntent应用,新增一条crime记录,然后长按删除它。(要在模拟器上模拟长按动作,可按下鼠标左键不放直到菜单弹出。)

18.3 实施上下文操作模式

通过浮动上下文菜单删除crime记录的实现代码,可在任何Android设备上运行。例如,图18-2为Jelly Bean系统设备上弹出的浮动菜单。

然而,在新系统设备上,长按视图进入上下文操作模式是提供上下文操作的主流方式。屏幕进入上下文操作模式时,上下文菜单中定义的菜单项会出现在覆盖着操作栏的上下文操作栏上,如图 18-3所示。相比浮动菜单,上下文操作栏不会遮挡屏幕,因此是更好的菜单展现方式。

18-3 长按列表项出现上下文操作

上下文操作栏的实现代码不同于浮动上下文菜单。此外,上下文操作栏实现代码所使用的类和方法不支持Froyo或Gingerbread等老系统,因此必须保证仅支持新系统的代码在老系统上不会被调用。

18.3.1 实现列表视图的多选操作

列表视图进入上下文操作模式时,可开启它的多选模式。多选模式下,上下文操作栏上的任何操作都将同时应用于所有已选视图。

CrimeListFragment.onCreateView(...)方法中,设置列表视图的选择模式为CHOICE_MODE_MULTIPLE_MODAL,如代码清单18-6所示。最后,为处理兼容性问题,记得使用编译版本常量,将登记ListView的代码与设置选择模式的代码区分开来。

代码清单18-6 设置列表视图的选择模式(CrimeListFragment.java)

18.3.2 列表视图中的操作模式回调方法

接下来,为ListView设置一个实现AbsListView.MultiChoiceModeListener接口的监听器。该接口包含以下回调方法,视图在选中或撤销选中时会触发它:

public abstract void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked)

MultiChoiceModeListener实现了另一个接口,即ActionMode.Callback。用户屏幕进入上下文操作模式时,会创建一个ActionMode类实例。随后在其生命周期内,ActionMode.Callback接口的回调方法会在不同时点被调用。以下为ActionMode.Callback接口中必须实现的四个方法:

public abstract boolean onCreateActionMode(ActionMode mode, Menu menu)

ActionMode对象创建后调用。也是实例化上下文菜单资源,并显示在上下文操作栏上的任务完成的地方。

public abstract boolean onPrepareActionMode(ActionMode mode, Menu menu)

onCreateActionMode(...)方法之后,以及当前上下文操作栏需要刷新显示新数据时调用。

public abstract boolean onActionItemClicked(ActionMode mode, MenuItem item)

在用户选中某个菜单项操作时调用。是响应上下文菜单项操作的地方。

public abstract void onDestroyActionMode(ActionMode mode)

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

CrimeListFragment.onCreateView()方法中,为列表视图设置实现MultiChoiceModeListener接口的监听器。这里,只需实现onCreateActionMode()onActionItemClicked(ActionMode, MenuItem)方法即可,如代码清单18-7所示。(: import android.view.ActionMode;)

代码清单18-7 设置MultiChoiceModeListener监听器(CrimeListFragment.java)

注意,如使用代码自动补全来创建MultiChoiceModeListener接口,系统自动产生的onCreateActionMode(...)存根方法会返回false值。记得将其改为返回true值,因为返回false值会导致操作模式创建失败。

另外要注意的是,在onCreateActionMode(...)方法中,我们是从操作模式,而非activity中获取MenuInflater的。操作模式负责对上下文操作栏进行配置。例如,可调用ActionMode.setTitle(...)方法为上下文操作栏设置标题,而activity的MenuInflater则做不到这一点。

接下来,在onActionItemClicked(...)方法中,响应菜单项删除操作,从CrimeLab中删除一个或多个Crime对象,然后重新加载显示列表。最后,调用ActionMode.finish()方法准备销毁操作模式。

运行CriminalIntent应用。长按选择任意列表项,进入上下文操作模式。此时,还要选择其他列表项的话,直接点击即可。而再次点击已选中的列表项则撤销选择。点击删除图标将结束操作模式并返回到刷新后的列表项界面。也可以点击上下文操作栏最左边的取消图标,这将取消操作模式并返回到没有任何变化的列表项界面。

如图18-4所示,尽管功能使用上没有什么问题,但用户的使用体验很糟糕,因为很难看出哪些列表项被选中了。不过,该问题可通过改变已选中列表项背景的方式解决。

18-4 第二crime记录已被选中

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

依据自身的不同状态,有时需要差别化地显示某个视图。CriminalIntent应用中,在列表项处于激活状态时,我们希望能够改变其显示背景。视图处于激活状态,是指该视图已被用户标记为关注处理对象。

基于视图的状态,可使用state list drawable资源来改变其显示背景。state list drawable是一种以XML定义的资源。该资源定义中,我们指定一个drawable(位图或彩图),并列出该drawable对应的状态。(可查阅StateListDrawable参考手册页,了解更多视图相关状态。)

右键点击res\drawable目录,选择New-> Drawable resource file,创建名为background_activated的资源文件文件。参照代码清单18-8完成内容的添加。

代码清单18-8 简单的state list drawable资源(res/drawable/background_activated.xml)

以上XML文件告诉我们:当引用该drawable资源的视图处于激活状态时,则使用android:drawable属性值指定的资源;反之,则不采取任何操作。如android:state_activated的属性值设置为false,则只要视图未处于激活状态,android:drawable指定的资源都会被使用。

修改res/layout/list_item_crime.xml文件,引用drawable目录下background_activated.xml定义的资源,如代码清单18-9所示。

代码清单18-9 改变列表项的显示背景(res/layout/list_item_crime.xml)

重新运行CriminalIntent应用。这次,已选列表项一眼便能看出了,如图18-5所示。

18-5 醒目的第二列表项

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

本章实现的上下文菜单栏可以完美地应用于ListView和GridView。(GridView是AdapterView的子类,第26章中会用到它。)但如果视图既非ListView,也非GridView,要使用上下文操作栏又该如何处理呢?

首先,设置一个实现View.OnLongClickListener接口的监听器。然后在监听器实现体内,调用Activity.startActionMode(...)方法创建一个ActionMode实例。(前面已经看到,如果使用的是MultiChoiceModeListener接口,ActionMode实例是自动创建的。)

startActionMode(...)方法需要一个实现ActionMode.Callback接口的对象作为参数。因此,创建一个ActionMode.Callback接口的实现,该接口实现自然也包括前面使用过的四个ActionMode生命周期方法:

public abstract boolean onCreateActionMode(ActionMode mode, Menu menu)
public abstract boolean onPrepareActionMode(ActionMode mode, Menu menu)
public abstract boolean onActionItemClicked(ActionMode mode, MenuItem item)
public abstract void onDestroyActionMode(ActionMode mode)

具体实现时,可先创建一个实现ActionMode.Callback接口的对象,然后在调用startActionMode(...)方法时传入,或直接调用startActionMode(...)方法并传入一个匿名实现参数。

时间: 2024-10-04 21:12:44

第 18 章 上下文菜单与上下文操作模式的相关文章

安卓开发复习笔记——Menu菜单组件(选项菜单,上下文菜单,子菜单)

菜单是用户界面中最常见的元素之一,使用非常频繁,在Android中,菜单被分为如下三种,选项菜单(OptionsMenu).上下文菜单(ContextMenu)和子菜单(SubMenu). 菜单的实现方式有2种:一种是通过布局文件xml生成菜单,另一种是通过代码生成. 三种菜单内容有点多,不过大体相似,一次性讲完吧,本人偏好代码动态生成,下面就以代码为例. 1.选项菜单(OptionsMenu) 先来看下选项菜单的效果图:   在一个Activity界面中点击手机Menu键,在屏幕下方弹出的菜单

Android开发系列(二十九):使用ContextMenu创建上下文菜单

在上一篇我们介绍了创建选项菜单,这里我们介绍下创建上下文菜单. 上下文菜单就是通过长按某一段文字,然后出来相应的菜单.就比如贴吧,当你长安某一楼层,就会弹出来一个"复制"这一楼层文字的菜单,点一下"复制",这段文字就保存在你手机的临时存储的地方了,可以在别的地方黏贴. 开发上下文菜单的步骤: 1.重写Activity的onCreateContextMenu(ContextMenu menu,View source,ContextMenu.Context MenuIn

Android 菜单 之 上下文菜单ContextMenu

所谓上下文菜单就是当我们长按某一个文件时弹出的菜单 操作这个菜单我们要重写onCreateContextMenu()方法 如上一篇文章一样,对于这个菜单中选型的操作也有动态添加和xml文件添加两种方法 首先是动态添加 @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v

ContextMenu上下文菜单

上下文菜单一般是真针对ListView(多条数据的操作) 需求:在ListView中显示通话记录的电话号码,长按显示的上下文菜单为复制号码到拨号盘.发送信息.复制号码,与之相对应的事件. 布局代码: 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_he

安卓开发_浅谈ContextMenu(上下文菜单)

长下文菜单,即长按view显示一个菜单栏 与OptionMenu的区别OptionMenu对应的是activity,一个activity只能拥有一个选项菜单ContextMenu对应的是View,每个View都可以设置上下文菜单一般情况下ContextMenu常用语ListView或者GridView; 创建和响应上下文菜单过程: 1.在activity的onCreate(...)方法中为一个view注册上下文菜单 2.在onCreateContextMenuInfo(...)中生成上下文菜单.

android,关于上下文菜单ContextMenu数据值的转递

在长按某view时,如该view有绑定OnCreateContextMenuListener,则会弹出ContextMenu上下文菜单,这类似于右键菜单,菜单弹出后点击某菜单项,则Activity或Fragment中的 public boolean onContextItemSelected(MenuItem item) 方法会响应菜单的点击,根据item的id来确定点击的是哪一个菜单项,从而进行进一步的操作. 那么现在的问题是,有时候我们只知道了点击的哪个菜单项是不够的,还需要知道更多的信息,

Android:WebView中对图片注册上下文菜单

前言 今天一朋友问我一个问题,就是如何在WebView控件中的图片增加上下文菜单,以便增加保存图片等功能.今天就给他简单做了一个演示Demo,现写下来,给有相同问题的朋友提供些许思路吧. 概要实现 其实这个功能很简单,没有太复杂的东西,就是对WebView的控件的使用,一是给WebView注册了上下文菜单事件,二是在响应事件中去判断事件源的类型,如果是图片类型,则把url取出来 注册上下文菜单事件 这个就比较简单了通过下面的代码即可完成. WebView vw = (WebView) findV

菜单(四)给ListView的item添加上下文菜单

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBo

Android上下文菜单ContentView详解

ContentView介绍 上下文菜单继承了android.view.Menu,因此我们可以像操作Options Menu那样给上下文菜单增加菜单项.上下文菜单与Options Menu最大的不同在于,Options Menu的拥有者是Activity,而上下文菜单的拥有者是Activity中的View.每个Activity有且只有一个Options Menu,它为整个Activity服务.而一个Activity往往有多个View,并不是每个View都有上下文菜单,这就需要我们显示地通过 reg