前言
今天看了pro android 3中menu這一章,對Android的整個menu體系有了進一步的了解,故整理下筆記與大家分享。
PS:強烈推薦《Pro Android 3》,是我至今為止看到的最好的一本android書,中文版出到《精通Android 2》。
理解Android的菜單
菜單是許多應用程序不可或缺的一部分,Android中更是如此,所有搭載Android系統的手機甚至都要有一個"Menu"鍵,由此可見菜單在Android程序中的特殊性。Android SDK提供的菜單有如下幾種:
- 選項菜單:最常規的菜單,android中把它叫做option menu
- 子菜單:android中點擊子菜單將彈出懸浮窗口顯示子菜單項。子菜單不支持嵌套,即子菜單中不能再包括其他子菜單。
- 上下文菜單:android中長按視圖控件后出現的菜單,windows點擊右鍵彈出的菜單即上下文菜單
- 圖標菜單:這個比較簡單,就是帶icon的菜單項,需要注意的是子菜單項、上下文菜單項、擴展菜單項均無法顯示圖標。
- 選擇菜單(alternative menu):用的比較少,以后單獨介紹,本文先跳過(其實是我還沒弄明白啦o(≧v≦)o~~)
- 擴展菜單:選項菜單最多只能顯示6個菜單項,超過6個時,第6個菜單項會被系統替換為一個叫“更多”的子菜單,原來顯示不下的菜單項都作為“更多”菜單的子菜單項。如下圖:

第6個菜單項自動變為“更多” 點擊“更多”顯示其他菜單項
Android3.0又引入了一個叫action bar的東西,本文不做講解,大家自己google。
android.view.Menu接口代表一個菜單,android用它來管理各種菜單項。注意我們一般不自己創建menu,因為每個Activity默認都自帶了一個,我們要做的是為它加菜單項和響應菜單項的點擊事件。android.view.MenuItem代表每個菜單項,android.view.SubMenu代表子菜單。其三者的關系可以用下圖來表示

上面說過,每個activity包含一個菜單,一個菜單又能包含多個菜單項和多個子菜單,子菜單其實也是菜單(因為它實現了Menu接口),因此子菜單也可以包含多個菜單項。SubMenu繼承了Menu的addSubMenu()方法,但調用時會拋出運行時錯誤。OnCreateOptionsMenu()和OnOptionsMenuSelected()是activity中提供了兩個回調方法,用於創建菜單項和響應菜單項的點擊。
創建options menu
之前提到,Android的activity已經為我們提前創建好了android.view.Menu對象,並提供了回調方法onCreateOptionsMenu(Menu menu)供我們初始化菜單的內容。該方法只會在選項菜單第一次顯示的時候被執行,如果你需要動態改變選項菜單的內容,請使用 onPrepareOptionsMenu(Menu)。
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// 調用父類方法來加入系統菜單
// 雖然目前android還沒有系統菜單,但是為了兼容到以后的版本,最好加上
super.onCreateOptionsMenu(menu);
// 添加菜單項(多種方式)
// 1.直接指定標題
menu.add("菜單項1");
// 2.通過資源指定標題
menu.add(R.string.menuitem2);
// 3.顯示指定菜單項的組號、ID、排序號、標題
menu.add(
1, //組號
Menu.FIRST, //唯一的ID號
Menu.FIRST, //排序號
"菜單項3"); //標題
// 如果希望顯示菜單,請返回true
returntrue;
}
add(int groupId, int itemId, int order, CharSequence title)
上面的代碼演示了添加菜單項的3種方法,下面解釋下第三種方法。其中,第一個參數是組號,android中你可以給菜單分組,以便快速地操作同一組的菜單。第二個參數指定每個菜單項的唯一ID號,你可以自己指定,也可以讓系統來自動分配,在響應菜單時你需要通過ID號來判斷哪個菜單被點擊了。因此常規的做法是定義一些ID常量,但在android中有更好的方法,就是通過資源文件來引用,這個之后介紹。第三個參數代表菜單項顯示順序的編號,編號小的顯示在前面。
給菜單項分組
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// 添加4個菜單項,分成2組
int group1 =1;
int gourp2 =2;
menu.add(group1, 1, 1, "item 1");
menu.add(group1, 2, 2, "item 2");
menu.add(gourp2, 3, 3, "item 3");
menu.add(gourp2, 4, 4, "item 4");
// 顯示菜單
returntrue;
}
你可以向上面這樣給菜單項分組,分組之后就能使用menu中提供的方法對組進行操作了,如下:
menu.removeGroup(group1); //刪除一組菜單
menu.setGroupVisible(gourp2, visible); //設置一組菜單是否可見
menu.setGroupEnabled(gourp2, enabled); //設置一組菜單是否可點
menu.setGroupCheckable(gourp2, checkable, exclusive); //設置一組菜單的勾選情況
響應菜單項
android提供了多種響應菜單項的方式,下面一一介紹
1、通過onOptionsItemSelected方法
使用的最多方法是重寫activity類的 onOptionsItemSelected(MenuItem)回調方法,每當有菜單項被點擊時,android就會調用該方法,並傳入被點擊菜單項。
@Override
publicboolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
//響應每個菜單項(通過菜單項的ID)
case1:
// do something here
break;
case2:
// do something here
break;
case3:
// do something here
break;
case4:
// do something here
break;
default:
//對沒有處理的事件,交給父類來處理
returnsuper.onOptionsItemSelected(item);
}
//返回true表示處理完菜單項的事件,不需要將該事件繼續傳播下去了
returntrue;
}
以上代碼可作為使用onOptionsItemSelected方法響應菜單的模板來使用,這里為了方便起見將菜單ID硬編碼在程序里,你可以使用常量或資源ID來使代碼更健壯。
2.使用監聽器
雖然第一種方法是推薦使用的方法,android還是提供了類似java swing的監聽器方式來響應菜單。使用監聽器的方式分為兩步:
//第一步:創建監聽器類
class MyMenuItemClickListener implements OnMenuItemClickListener {
@Override
publicboolean onMenuItemClick(MenuItem item) {
// do something here...
returntrue; //finish handling
}
}
//第二步:為菜單項注冊監聽器
menuItem.setOnMenuItemClickListener(new MyMenuItemClickListener());
android文檔對onMenuItemClick(MenuItem item)回調方法的說明是"Called when a menu item has been invoked. This is the first code that is executed; if it returns true, no other callbacks will be executed." 可見該方法先於onOptionsItemSelected執行。
3.使用Intent響應菜單
第3種方式是直接在MenuItem上調用setIntent(Intent intent)方法,這樣android會自動在該菜單被點擊時調用 startActivity(Intent)。但是個人認為與其這樣還不如直接在onOptionsItemSelected的case里手動調用 startActivity(Intent)來的直觀。
Android菜單詳解(三)——SubMenu和IconMenu
我們在上一篇介紹了如何在Android中創建和響應選項菜單,今天我們將探索子菜單和圖標菜單。
子菜單Sub Menu
子菜單提供了一種自然的組織菜單項的方式,它被大量地運用在windows和其他OS的GUI設計中。Android同樣支持子菜單,你可以通過addSubMenu(int groupId, int itemId, int order, int titleRes)方法非常方便的創建和響應子菜單。
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
int base = Menu.FIRST;
// 一個menu可以包括多個子菜單
SubMenu subMenu = menu.addSubMenu(base, base+1, Menu.NONE, "系統設置");
// 子菜單可以包括多個菜單項
MenuItem menuitem1 = subMenu.add(base, base+1, base+1, "顯示設置");
subMenu.add(base, base+2, base+2, "網絡設置");
subMenu.add(base, base+3, base+3, "高級設置");
subMenu.add(base, base+4, base+4, "安全設置");
// 子菜單項不支持顯示圖標,這樣做是沒意義的,盡管不會報錯!
menuitem1.setIcon(R.drawable.displaysettings);
//但是子菜單本身是支持圖標的
subMenu.setIcon(R.drawable.settings);
// 顯示菜單請返回true
returntrue;
}

上面的代碼演示了如何創建子菜單,其菜單項的響應其實就是普通菜單項的響應,上一篇已經作了詳細介紹,這里不再贅述。Android中可以為子菜單添加圖標,但是不會顯示其菜單項的圖標,這一點需要留意。除了代碼中的setIcon(int iconRes)方法,還有一個setHeaderIcon(int iconRes)方法可以添加子菜單項欄目的標題圖標,效果如上面第三張圖。
最后需要強調的是,Menu可以包含多個SubMenu,SubMenu可以包含多個MenuItem(這三者之間的關系見Android菜單詳解(一)——理解Android中的Menu),但是SubMenu不能包含SubMenu,及子菜單不能嵌套!!!下面的代碼能通過編譯,但會在運行時出錯。
subMenu.addSubMenu("又一個子菜單");
圖標菜單Icon Menu
Android支持在菜單上顯示各種各樣的圖標,這一點我們在上面創建子菜單時已經用到了。圖標菜單嚴格上說並不算是一種菜單的新類型,它的使用也很簡單,之所以單獨設一節是為了說明使用Icon的一些限制。Android中並不是所謂的菜單項都能加上圖標,以下菜單項都是不可以的(這並不意味着程序會報錯,而是運行時圖標得不到顯示):
- 擴展的菜單項。如果不理解什么是擴展的菜單,可以參考Android菜單詳解(一)——理解Android中的Menu
- 子菜單的菜單項
- 上下文菜單的菜單項
除此以外,帶Icon的菜單項不能加上復選框(check mark)。總之,雖然精美的圖標能給我們的應用增色不少,但是濫用圖標也是會適得其反的,Android SDK給圖標菜單加的這些限制也算是有效的防止我們濫用圖標了吧。
結語
本篇介紹了Android中的子菜單和給菜單項加Icon時需要注意的幾點,下一篇《Android菜單詳解(四)——使用上下文菜單ContextMenu》將介紹上下文菜單Context Menu的使用。
之前在《Android菜單詳解(二)——創建並響應選項菜單》和《Android菜單詳解(三)——SubMenu和IconMenu》中詳細講解了選項菜單,子菜單和圖標菜單。今天接着細說另一種被廣泛使用的菜單——上下文菜單Context Menu。
ContextMenu簡介
在Windows中,我們已經習慣了在文件上單擊右鍵來執行“打開”、“重名名”、“剪切”、“刪除”等操作,這個右鍵彈出的菜單就是上下文菜單。你可能會笑道:“哈哈,你不會連快捷鍵都不會用吧?”。咳咳,這個。。。舉個例子嘛。沒錯,windows中快捷鍵能幫助我們提高操作的效率,但是android中這招可不管用嘍,注意:android的上下文菜單項是不能用快捷鍵的。因為手機的操作方式與使用鼠標的PC操作方式不同,android是通過長按某個視圖元素來彈出上下文菜單的(PS:現在大多數智能機是全觸屏的,沒有物理鍵盤,更沒有使用快捷鍵的需要了,這項革新要歸功於喬布斯在07發布的革命手機iPhone,老喬退休了,向他致敬!)。除此之外,甚至連圖標和子菜單都無法用在Android的上下文菜單項中。那么,Android的上下文菜單到底如何使用?見下圖

如圖,上下文菜單繼承了android.view.Menu,因此我們可以像操作Options Menu那樣給上下文菜單增加菜單項。上下文菜單與Options Menu最大的不同在於,Options Menu的擁有者是Activity,而上下文菜單的擁有者是Activity中的View。每個Activity有且只有一個Options Menu,它為整個Activity服務。而一個Activity往往有多個View,並不是每個View都有上下文菜單,這就需要我們顯示地通過registerForContextMenu(View view)來指定。
盡管上下文菜單的擁有者是View,生成上下文菜單卻是通過Activity中的onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法,該方法很像生成Options Menu的onCreateOptionsMenu(Menu menu)方法。兩者的不同在於,onCreateOptionsMenu只在用戶第一次按“Menu”鍵時被調用,而onCreateContextMenu會在用戶每一次長按View時被調用,而且View必須已經注冊了上下文菜單。
另一個值得注意的就是上圖中的ContextMenuInfo,該類的對象被傳入onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法,那么它有什么用呢?有時候,視圖元素需要向上下文菜單傳遞一些信息,比如該View對應DB記錄的ID等,這就要使用ContextMenuInfo。需要傳遞額外信息的View需要重寫getContextMenuInfo()方法,返回一個帶有數據的ContextMenuInfo實現類對象。
介紹了這么多,下面給出一個demo演示如何創建和響應上下文菜單:
1.在activity的onCreate(...)方法中為一個view注冊上下文菜單
2.在onCreateContextMenuInfo(...)中生成上下文菜單。
3.在onContextItemSelected(...)中響應上下文菜單項。
Demo:使用上下文菜單

1)注冊上下文菜單
/**
* 上下文菜單演示Demo
*
* @author CodingMyWorld 2011-8-27 下午03:22:39
*/
publicclass SampleContextMenuActivity extends ListActivity {
privatestaticfinal String TAG ="SampleContextMenuActivity";
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 顯示列表
simpleShowList();
// 為所有列表項注冊上下文菜單
this.registerForContextMenu(getListView());
}
privatevoid simpleShowList() {
// list item
String[] files =new String[] {
"文件1",
"文件2",
"文件3",
"文件4"
};
// simple array adapter
ArrayAdapter<String> adapter =new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
files);
// set adapter
this.setListAdapter(adapter);
Log.v(TAG, "show simple list");
}
}
2)生成上下文菜單
在activity中重寫方法。
@Override
publicvoid onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
Log.v(TAG, "populate context menu");
// set context menu title
menu.setHeaderTitle("文件操作");
// add context menu item
menu.add(0, 1, Menu.NONE, "發送");
menu.add(0, 2, Menu.NONE, "標記為重要");
menu.add(0, 3, Menu.NONE, "重命名");
menu.add(0, 4, Menu.NONE, "刪除");
}
3)響應上下文菜單項
與響應options menu類似,唯一的不同是可以通過menu info獲得額外的信息。
@Override
publicboolean onContextItemSelected(MenuItem item) {
// 得到當前被選中的item信息
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
Log.v(TAG, "context item seleted ID="+ menuInfo.id);
switch(item.getItemId()) {
case1:
// do something
break;
case2:
// do something
break;
case3:
// do something
break;
case4:
// do something
break;
default:
returnsuper.onContextItemSelected(item);
}
returntrue;
}
運行程序,多次喚出上下文菜單,注意LogCat的輸出,onCreateContextMenu每一次都被調用了。
結語
至此,我們介紹完了android中的各種常用菜單,但是目前為止我們都是通過硬編碼來增加菜單項的,android為此提供了一種更便利的方式,將在下一節“使用XML生成菜單”中介紹。
Android菜單詳解(五)——使用XML生成菜單
回顧前面的幾篇,我們都是直接在代碼中添加菜單項,給菜單項分組等,這是比較傳統的做法,它存在着一些不足。比如說,為了響應每個菜單項,我們需要用常量來保存每個菜單項的ID等。為此,Android提供了一種更好的方式,就是把menu也定義為應用程序的資源,通過android對資源的本地支持,使我們可以更方便地實現菜單的創建與響應。這一篇就介紹如何使用XML文件來加載和響應菜單,我們需要做這幾步:
- 在/res目錄下創建menu文件夾
- 在menu目錄下使用與menu相關的元素定義xml文件,文件名是隨意的,android會自動為其生成資源ID。例如:R.menu.mainmenu對應menu目錄的mainmenu.xml資源文件
- 使用xml文件的資源ID,將xml文件中定義的菜單項添加到menu對象中
- 響應菜單項時,使用每個菜單項對應的資源ID
下面就使用xml的方式完成《Android菜單詳解(二)——創建並響應選項菜單》中的options menu。
定義菜單資源文件
在res目錄下創建menu文件夾,在menu下創建一個xml資源文件,我這里叫做mainmenu.xml

編寫mainmenu.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- group1 -->
<group android:id="@+id/group1">
<item android:id="@+id/mi1"
android:title="item1"/>
<item android:id="@+id/mi2"
android:title="item2"/>
</group>
<!-- group 2 -->
<group android:id="@+id/group2">
<item android:id="@+id/mi3"
android:title="item3"/>
<item android:id="@+id/mi4"
android:title="item4"/>
</group>
</menu>
這里簡單的添加了4個菜單項,並將其分為2組。item元素的android:title值可以引用values中的string資源。
使用MenuInflater添加菜單項
inflater在android中建立了從資源文件到對象的橋梁,MenuInflater即把菜單xml資源轉換為對象並添加到menu對象中,它可以通過activity的getMenuInflater()得到。我們在MainActivity中重寫onCreateOptionsMenu(...)方法。
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
returntrue;
}
響應菜單項
最后重寫onOptionsItemSeleted(...)方法。
@Override
publicboolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.mi1:
// do sth
break;
case R.id.mi2:
// do sth
break;
case R.id.mi3:
// do sth
break;
case R.id.mi4:
// do sth
break;
}
returntrue;
}
這一步最大的體現了使用XML生成菜單的好處。android不僅為我們定義的xml文件生成了資源ID,同樣可以為group,menu item來自動生成ID(就像為布局中定義的view生成id那樣)。這樣菜單項ID的創建與管理就不用我們操心了,全部交給android去做吧!
至此,我們完成了一個簡單的“使用XML生成菜單”的demo,並從中體會到了使用資源文件的好處,因此這是android中創建菜單的推薦方式。實際上,我們在代碼中對菜單項或分組的操作都能在xml文件中完成,下面就簡單介紹一些比較常用的功能。(谷歌提供的API demos中有最全面的示例)
更多菜單資源文件的功能
1.資源文件實現子菜單
子菜單通過在item元素中嵌套menu來實現。
<item android:title="系統設置">
<menu>
<item android:id="@+id/mi_display_setting"
android:title="顯示設置"/>
<item android:id="@+id/mi_network_setting"
android:title="網絡設置"/>
<!-- 別的菜單項 -->
</menu>
</item>
2.為菜單項添加圖標
<item android:id="@+id/mi_exit"
android:title="退出"
android:icon="@drawable/exit"/>
3.設置菜單項的可選出現
使用android:checkableBehavior設置一組菜單項的可選策略,可選值為:none, all, single
<group android:id="..."
android:checkableBehavior="all">
<!-- 菜單項 -->
</group>
使用android:checked設置特定菜單項
<item android:id="..."
android:title="sometitle"
android:checked="true"/>
4.設置菜單項可用/不可用
<item android:id="..."
android:title="sometitle"
android:enabled="false"/>
5.設置菜單項可見/不可見
<item android:id="..."
android:title="sometitle"
android:visible="false"/>
結語
本篇是Android菜單詳解系列的最后一篇,本系列詳細介紹了android中各種常用菜單的使用技巧和注意事項,希望能幫助大家更好的理解,也期待與各位交流開發中的心得:)

