前言
今天看了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中提供了兩個回調方法,用於創建菜單項和響應菜單項的點擊。
Android菜單詳解(二)——創建並響應選項菜單
上一篇《Android菜單詳解(一)——理解android中的menu》簡單介紹了一下Android的菜單,今天讓我們看一下如何通過代碼創建和響應最常用的選項菜單(options menu)。
創建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(MenuItemitem)回調方法的說明是"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菜單詳解(四)——使用上下文菜單ContextMenu
之前在《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中各種常用菜單的使用技巧和注意事項,希望能幫助大家更好的理解,也期待與各位交流開發中的心得:)