android 笔记2
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 函数。
.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, 这样会覆盖掉
.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”
@Overridepublic 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 ;
}
然后,设置上下文菜单的标题为日志标题
// Setupthemenuheadermenu.setHeaderTitle(cursor.getString(COLUMN_INDEX_TITLE));
最后为上下文菜单增加一个菜单项
// Addamenuitemtodeletethenotemenu.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 可以在桌面上加很多的图标,分别启动内部不同的多个界面。 “ ,实验后发现确实如此,学习了。
更多相关文章
- Android中对Log日志文件的分析
- Android(安卓)R文件丢失解决办法
- Android实例剖析笔记(二)
- ListView美化(2)-android:cacheColorHint,listSelect...
- Android(安卓)控件及其属性2
- Android(安卓)ListView 去除底色、选中色、阴影
- Android(安卓)菜单(OptionMenu)
- android中调试之日志
- Android(安卓)菜单(OptionMenu)