Phinecos(洞庭散人) 专注于开源技术的研究与应用

Android实例剖析笔记(二)

上篇文章 分析了 NotesList 这个 Activity ,并着重剖析了其中的 intent 机制,本文将继续上篇未完的工作,以 NotesList 为实例介绍 Android 的菜单机制(尤其是动态菜单机制)。

简介

android 提供了三种菜单类型,分别为 options menu context menu sub menu

options menu 就是通过按 home 键来显示, context menu 需要在 view 上按上 2s 后显示。这两种 menu 都有可以加入子菜单,子菜单不能种不能嵌套子菜单。 options menu 最多只能在屏幕最下面显示 6 个菜单选项,称为 icon menu icon menu 不能有 checkable 选项。多于 6 的菜单项会以 more icon menu 来调出,称为 expanded menu options menu 通过 activity onCreateOptionsMenu 来生成,这个函数只会在 menu 第一次生成时调用。任何想改变 options menu 的想法只能在 onPrepareOptionsMenu 来实现,这个函数会在 menu 显示前调用。 onOptionsItemSelected 用来处理选中的菜单项。

context menu 是跟某个具体的 view 绑定在一起,在 activity 种用 registerForContextMenu 来为某个 view 注册 context menu context menu 在显示前都会调用 onCreateContextMenu 来生成 menu onContextItemSelected 用来处理选中的菜单项。

android 还提供了对菜单项进行分组的功能,可以把相似功能的菜单项分成同一个组,这样就可以通过调用 setGroupCheckable setGroupEnabled,setGroupVisible 来设置菜单属性,而无须单独设置。

Options Menu

Notepad 中使用了 options menu context menu 两种菜单。首先来看生成 options menu onCreateOptionsMenu 函数。

menu.add( 0 ,MENU_ITEM_INSERT, 0 ,R.string.menu_insert)
.setShortcut(
' 3 ' , ' a ' )
.setIcon(android.R.drawable.ic_menu_add);

这是一个标准的插入一个菜单项的方法,菜单项的 id MENU_ITEM_INSERT

有意思的是下面这几句代码:

Intentintent = new Intent( null ,getIntent().getData());
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE,
0 , 0 ,
new ComponentName( this ,NotesList. class ), null ,intent, 0 , null );

这到底有何用处呢?其实这是一种动态菜单技术 ( 也有点像插件机制 ) ,若某一个 activity ,其类型是 ”android.intent.category.ALTERNATIVE” ,数据是 ”vnd.android.cursor.dir/vnd.google.note” 的话,系统就会为这个 activity 增加一个菜单项。在 androidmanfest.xml 中查看后发现,没有一个 activity 符合条件,所以这段代码并没有动态添加出任何一个菜单项。

为了验证上述分析,我们可以来做一个实验,在 androidmanfest.xml 中进行修改,看是否会动态生成出菜单项。

实验一

首先我们来创建一个新的 activity 作为目标 activity, 名为 HelloAndroid, 没有什么功能,就是显示一个界面。

public class HelloAndroid extends Activity{
@Override
protected void onCreate(BundlesavedInstanceState){
super .onCreate(savedInstanceState);
this .setContentView(R.layout.main);
}
}

它所对应的布局界面 XML 文件如下:

<? xmlversion="1.0"encoding="utf-8" ?>
< LinearLayout xmlns:android ="http://schemas.android.com/apk/res/android"
android:orientation
="vertical"
android:layout_width
="fill_parent"
android:layout_height
="fill_parent"
>
< TextView
android:layout_width ="fill_parent"
android:layout_height
="wrap_content" android:id ="@+id/TextView01" />

< Button android:id ="@+id/Button01" android:layout_height ="wrap_content" android:layout_width ="fill_parent" android:text ="@string/txtInfo" ></ Button >
</ LinearLayout >

然后修改 androidmanfest.xml ,加入下面这段配置,让 HelloAndroid 满足上述两个条件:

< activity android:name ="HelloAndroid" android:label ="@string/txtInfo" >
< intent-filter >
< action android:name ="com.android.notepad.action.HELLO_TEST" />
< category android:name ="android.intent.category.ALTERNATIVE" />
< data android:mimeType ="vnd.android.cursor.dir/vnd.google.note" />
</ intent-filter >
</ activity >

好了,运行下试试,哎,还是没有动态菜单项加入呀!

怎么回事呢?查看代码后发现,原来是 onPrepareOptionsMenu 搞的鬼!这个函数在 onCreateOptionsMenu 之后运行,下面这段代码中,由于 Menu.CATEGORY_ALTERNATIVE 是指向同一个组,所以把 onCreateOptionsMenu 中设置的菜单项给覆盖掉了, 而由于 onPrepareOptionsMenu 没有给 Menu.CATEGORY_ALTERNATIVE 附新值,故 Menu.CATEGORY_ALTERNATIVE 还是为空。

Intentintent = new Intent( null ,uri);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE,
0 , 0 , null ,specifics,intent, 0 ,items);

好的,那我们暂时把上面这几句给注释掉,当然,也可以不注释这几句, onCreateOptionsMenu 中改 groupid 号,即将 Menu.CATEGORY_ALTERNATIVE 改为 Menu.first, 其他的也行,但注意不要改为 menu.none, 这样会覆盖掉

menu.add( 0 ,MENU_ITEM_INSERT, 0 ,R.string.menu_insert)
.setShortcut(
' 3 ' , ' a ' )
.setIcon(android.R.drawable.ic_menu_add);

添加的菜单。因为 menu.none 也为 0

运行后就可以看到动态菜单出来了!

上面这个 options menu 是在 NotesList 界面上没有日志列表选中的情况下生成的,若先选中一个日志,然后再点 ”menu” ,则生成的 options menu 是下面这样的:

哎,又动态增加了两个菜单项 ”Edit note” ”Edit title”, 这又是如何动态加入的呢?这就是 onPrepareOptionsMenu 的功劳了。

Uriuri = ContentUris.withAppendedId(getIntent().getData(),getSelectedItemId());

首先获取选中的日志(若没有选择,则 uri 为空)

Intent[]specifics = new Intent[ 1 ];
specifics[
0 ] = new Intent(Intent.ACTION_EDIT,uri);
MenuItem[]items
= new MenuItem[ 1 ];

然后为选中的日志创建一个 intent, 操作类型为 Intent. ACTION_EDIT ,数据为选中日志的 URI. 于是会为选中的日志创建一个 ”Edit note” 菜单项。

Intentintent = new Intent( null ,uri);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE,
0 , 0 , null ,specifics,intent, 0 ,
items);

这几句和上面 onCreateOptionsMenu 函数中类似,用于动态增加菜单项,若某一个 activity ,其类型是 ”android.intent.category.ALTERNATIVE” ,数据是 ”vnd.android.cursor.item/vnd.google.note” 的话,系统就会为这个 activity 增加一个菜单项。在 androidmanfest.xml 中查看后发现, TitleEditor 这个 activity 符合条件,于是系统就为 TitleEditor 这个 activity 动态添加一个菜单项 ”Edit title”

else {
menu.removeGroup(Menu.CATEGORY_ALTERNATIVE);
}

若日志列表为空,则从菜单中删除组号为 Menu. CATEGORY_ALTERNATIVE 的菜单项,只剩下 ”Add note” 菜单项。

处理 选中菜单项 事件

菜单项选中事件的处理非常简单,通过 onOptionsItemSelected 来完成 , 这里只是简单地调用 startActivity( new Intent(Intent. ACTION_INSERT , getIntent().getData())); 这个 intent 的操作类型为 Intent. ACTION_INSERT ,数据为日志列表的 URI ,即 ”content:// com.google.provider.NotePad/notes”

@Override
public boolean onOptionsItemSelected(MenuItemitem){
switch (item.getItemId()){
case MENU_ITEM_INSERT:
// Launchactivitytoinsertanewitem
startActivity( new Intent(Intent.ACTION_INSERT,getIntent().getData()));
return true ;
}
return super .onOptionsItemSelected(item);
}

Context Menu

下面介绍另一种菜单 --- 上下文菜单,这通过重载 onCreateContextMenu 函数实现。

首先确认已经选中了日志列表中的一个日志,若没选择,则直接返回。 Cursor 指向选中的日志项。

Cursorcursor = (Cursor)getListAdapter().getItem(info.position);
if (cursor == null ){
// Forsomereasontherequesteditemisn'tavailable,donothing
return ;
}

然后,设置上下文菜单的标题为日志标题

// Setupthemenuheader
menu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE));

最后为上下文菜单增加一个菜单项

// Addamenuitemtodeletethenote
menu.add( 0 ,MENU_ITEM_DELETE, 0 ,R.string.menu_delete);

对于上下文菜单项选中的事件处理,是通过重载 onContextItemSelected 实现的。

switch (item.getItemId()){
case MENU_ITEM_DELETE:{
// Deletethenotethatthecontextmenuisfor
UrinoteUri = ContentUris.withAppendedId(getIntent().getData(),info.id);
getContentResolver().delete(noteUri,
null , null );
return true ;
}
}
return false ;
}

对于日志的删除,首先调用 ContentUris.withAppendedId(getIntent().getData(), info.id); 来拼接出待删除日志的 URI. 然后 getContentResolver().delete(noteUri, null, null); 调用下层的 Content Provider 去删除此日志。

实验二

来做个简单实验,在上述代码基础上增加一个上下文菜单项。首先在 onCreateContextMenu 函数中增加一个上下文菜单项:

menu.add( 0 ,MENU_ITEM_INSERT, 0 ,R.string.menu_insert);

然后为其在 onContextItemSelected 函数中增加一个处理过程:

case MENU_ITEM_INSERT:
{
new AlertDialog.Builder( this ).setIcon(R.drawable.app_notes)
.setTitle(R.string.app_name).setMessage(R.string.error_message).setPositiveButton(R.string.button_ok,
new OnClickListener(){

public void onClick(DialogInterfacedialog, int which){
// TODOAuto-generatedmethodstub

}

}).show();
return true ;
}

实验结果如下:



附记

感谢 Evan JIANG 对前一篇文章的错误之处进行指正,

“<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
只是指明会在 Launcher 中显示图标,同一个 apk 可以在桌面上加很多的图标,分别启动内部不同的多个界面。 ,实验后发现确实如此,学习了。

更多相关文章

  1. Android中对Log日志文件的分析
  2. Android(安卓)R文件丢失解决办法
  3. Android实例剖析笔记(二)
  4. ListView美化(2)-android:cacheColorHint,listSelect...
  5. Android(安卓)控件及其属性2
  6. Android(安卓)ListView 去除底色、选中色、阴影
  7. Android(安卓)菜单(OptionMenu)
  8. android中调试之日志
  9. Android(安卓)菜单(OptionMenu)

随机推荐

  1. Android触摸事件分发机制
  2. 浅谈android的selector背景选择器
  3. Android(安卓)小项目之--SQLite 使用法门
  4. 移动端网络优化
  5. android和ios的系统特性区别
  6. android
  7. Cocos2d-x在win7下的android交叉编译环境
  8. anctionbar样式 自定义属性
  9. android沉浸式+虚拟按键+Fragment+Coordi
  10. android 事件流转机制