Android 學習筆記(二七:Menu
Menu由兩種形式,Option menu和Context menu。前者是按下裝置的Menu硬按鈕彈出,後者是長按widget彈出。
Option Menu
當我們按下Menu的硬體按鈕時,Option Menu將被觸發顯示,最多可以顯示6個選項的icon選單,如果選項多於6個,第6個選項顯示為“More“,點選可以進入擴充套件選單。我們將在Android學習筆記(十一):Activity-ListView的例子一的基礎上來學習Option Menu,也就是一個基於activity的選單。
在這個例子中,我們給出一個有7個選項(多餘最多顯示6個item)的例子,可以設定List中item之間分割線的粗細。
步驟1:建立Menu
1.1 設定Menu各個item的ID
private static final int EIGHT_ID = Menu.FIRST +1; private static final int SIXTEEN_ID = Menu.FIRST+2; private static final int TWENTY_FOUR_ID = Menu.FIRST+3; private static final int TWO_ID = Menu.FIRST+4; private static final int THIRTY_TWO_ID = Menu.FIRST+5; private static final int FORTY_ID = Menu.FIRST+6; private static final int ONE_ID = Menu.FIRST+7;
其中Menu.FIRST在reference中描述為:First value for group and item identifier integers.我們可以理解為ID設定的最小數值。
1.2 建立Menu
在使用者第一次按下Menu鍵的使用,將觸發onCreateOptionsMenu(),我們將在此建立我們的選單
public boolean onCreateOptionsMenu(Menu menu) { /*第一個引數是groupId,如果不需要可以設定為Menu.NONE。將若干個menu item都設定在同一個Group中,可以使用setGroupVisible(),setGroupEnabled(),setGroupCheckable()這樣的方法,而不需要對每個item都進行setVisible(), setEnable(), setCheckable()這樣的處理,這樣對我們進行統一的管理比較方便
* 第二個引數就是item的ID,我們可以通過menu.findItem(id)來獲取具體的item * 第三個引數是item的順序,一般可採用Menu.NONE,具體看本文最後MenuInflater的部分 * 第四個引數是顯示的內容,可以是String,或者是引用Strings.xml的ID*/ menu.add(Menu.NONE,ONE_ID,Menu.NONE,"1 Pixel"); menu.add(Menu.NONE, TWO_ID, Menu.NONE, "2 Pixels"); menu.add(Menu.NONE, EIGHT_ID, Menu.NONE, "8 Pixels"); menu.add(Menu.NONE, SIXTEEN_ID, Menu.NONE, "16 Pixels"); menu.add(Menu.NONE, TWENTY_FOUR_ID, Menu.NONE, "24 Pixels"); menu.add(Menu.NONE, THIRTY_TWO_ID, Menu.NONE, "32 Pixels"); menu.add(Menu.NONE, FORTY_ID, Menu.NONE, "40 Pixels"); return super.onCreateOptionsMenu(menu); }
如果我們需要增加圖示,也很簡單,如下。
MenuItem item1 = menu.add(Menu.NONE,ONE_ID,Menu.NONE,"1 Pixel"); item1.setIcon(R.drawable.android_normal);
執行,按Menu鍵,可以獲得我們的Menu,如下圖,第一個圖是顯示效果,第二個圖是按了選單中的More後顯示的效果,第三個圖我們在上面的基礎上,再增加三個item,按More後的顯示效果。
步驟2:Menu觸發
Menu觸發比較簡單,Activity在Memu後會觸發onOptionsItemSelected()進行處理。如下:
public boolean onOptionsItemSelected(MenuItem item) { ... ... 加入我們的處理 ... ... return super.onOptionsItemSelected(item); }
在這個例子我們加入的處理如下。右圖為選擇24pix的顯示結果
switch (item.getItemId()) { //獲取Id case ONE_ID: getListView().setDividerHeight(1); break; case EIGHT_ID: getListView().setDividerHeight(8); break; ... 類同,設定分割線的粗細,略去... default: break; }
步驟3:增加某些變化
在上面的步驟中,已經學習了Option Menu的基本處理,但是我們需要增加一些變化
3.1 每次顯示menu時根據實際的情況進行適配
onCreateOptionsMenu()只在第一次按Menu按鍵時觸發,有些時候,我們希望每次顯示的選單有一些變化,例如這個例子中,我們希望選單不顯示當前的分割線高度,只出現需要改變的高度,如圖所示,當分割線為16pix時,選單將不出現16pix的item。這樣可以使用onPrepareOptionsMenu()。當用戶第一次按Menu鍵時,先執行onCreateOptionsMenu( ),然後執行onPrepareOptionsMenu();當用戶第二次,第三次,第N次按Menu建時,執行onPrepareOptionsMenu(),因此我們可以在onPrepareOptionsMenu()中處理每次的變化。本例如下
public boolean onPrepareOptionsMenu(Menu menu) { /* 將所有的item設定有可視,好煩,想起了設定GourpID的好處,可以使用menu.setGroupVisible(Menu.NONE, true)代替。但是合理的,我們應當設定自己的GroupID,因此我們仍然每個item進行設定*/ menu.findItem(ONE_ID).setVisible(true); menu.findItem(EIGHT_ID).setVisible(true); ... 類同,設定menu item可視,略去... switch(getListView().getDividerHeight()){//如果需要設定的高度和當前的高度一致,不顯示。 case 1: menu.findItem(ONE_ID).setVisible(false); break; case 8: menu.findItem(EIGHT_ID).setChecked(true); break; ... 類同,略去... default: break; } return super.onPrepareOptionsMenu(menu); }
3.2 快捷鍵
現在的智慧手機一般都是直板不帶鍵盤的,但是傳統手機可能代T9鍵盤,也可能是全鍵盤。Android支援快捷鍵,雖然可能不常用到,對於全鍵盤,我們可以在onCreateOptionsMenu()中加入下面程式碼,這樣,在CTRL-A和Alt-A中都能觸發。
menu.findItem(ONE_ID).setAlphabeticShortcut('A');
對於T9鍵盤,由於模擬器不是T9鍵盤,也沒有T9的手機,最後的效果沒有實驗過,如下處理:
menu.setQwertyMode(true);menu.findItem(TWO_ID).setNumericShortcut('2');
3.3 設定Item顯示CheckBox的格式
我們選取了其中兩item進行設定,如下:
1)在onCreateOptionsMenu()中設定這兩個item是可以顯示的是否checked的狀態:
menu.findItem(EIGHT_ID).setCheckable(true); menu.findItem(FORTY_ID).setCheckable(true);
2)在onPrepareOptionsMenu()中,將如何和當前狀態一致這設定checked的狀態,例如:
case 8: menu.findItem(EIGHT_ID).setChecked(true); break;case 40: menu.findItem(FORTY_ID).setChecked(true); break;
3)什麼時候可以顯示
讓我們看看這兩個的顯示結果,我們發現“8 pix"的情況下Menu沒有發生變化,而選擇40的時候,出現了變化。為什麼會這樣?顯示的前提是有足夠位置顯示。在“8 pix“由於是Menu的第一頁的6個item,沒有足夠位置,而40px是More後的採用list的形式顯示,有足夠的位置,因此可以顯示。
同樣的,對於加Icom的例子,我們可以在第一頁中看到Icon,如果通過More的方式顯示後面的Icon,這個圖片是看不到的,Android會給據UI情況進行適配。
4)我們可以通過Group來處理:
menu.setGroupCheckable(Group_id, true, false);//在這個例子中可以使用Menu.NONE作為Group_Id來實驗
第二個引數是是否允許checkable,而第三個引數很有意思,true表示可以可以單選,採用radio button的方式,如下左圖,false表示可以多選,如下右圖。
步驟4:子選單
Android支援二級選單,但是不支援三級等多級選單。子選單設定如下,在onCreateOptionsMenu(),如下:
//通過addSubMenu設定子選單,作為item加入Menu。引數和addMenu一致,為了簡單,我們這裡的ID直接採用數字表示SubMenu submenu = menu.addSubMenu(Menu.NONE, 100, Menu.NONE, "子選單測試");//在SubMenu中增加子選單的itemsubmenu.add(Menu.NONE,101,Menu.NONE,"sub One");submenu.add(Menu.NONE,102,Menu.NONE,"sub Two");submenu.add(Menu.NONE,103,Menu.NONE,"sub Three");submenu.add(Menu.NONE,104,Menu.NONE,"sub Four");
顯示如下,我們是加在原有選單之後,因此需要按More檢視:
Context Menu
Context Menu是使用者手指長按某個View觸發的選單。處理如下:
步驟1:為某個view註冊ContextMenu
例如在我們的例子中,在onCreate()中對整個ListView進行處理:
registerForContextMenu(getListView());
步驟2:建立ContextMenu
通過Override onCreateContextMenu()來建立Context Menu。如果我們為多個View都註冊了ContextView,那麼我們可以通過第二個引數View v來判斷需要建立怎樣的選單。第三個引數ContextMenuInfo和具體的View的特性有關,如果是list,它是List這中的item,這樣我們可以根據這個item的當前狀態,例如是否checked來處理menu。
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { ... 設定Menu的處理,和Option Menu一樣 ....,同樣的支援子選單 super.onCreateContextMenu(menu, v, menuInfo); }
我們每一次常按widget,都會觸發onCreateContextMenu()的處理,這和Option Menu不一樣。每次處理完,ContextMenu都會discard,因此我們不要保留裡面的menu以及menu item物件用於其他的處理。
步驟3:點選選單觸發函式
觸發onContextItemSelected()。這裡麼只有一個MenuItem,因此在程式中,每個MenuItem的ID應該是唯一的,如果我們需要獲取MenuInfo,可以用item.getMenuInfo()來獲得。
public boolean onContextItemSelected(MenuItem item) { ... 我們的處理內容... return super.onContextItemSelected(item); }
通過XML來定義Menu
在Android學習筆記(十七):再談ListView中,利用LayoutInflater infalter =getLayoutInflater();從XML檔案中獲取Layout的樣式。在Menu中也可以採用類似的方式。我們在onCreateOptionsMenu()中如下處理:
public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = new MenuInflater(getApplication()); menuInflater.inflate(R.menu.chapter11_menu, menu); return super.onCreateOptionsMenu(menu); }
其中我們在res/menu目錄下面建立Menu的xml檔案chapter11_menu.xml。我們通過下面的例子看看Menu XML檔案如何編寫:
<?xml version="1.0" encoding="utf-8"?><!-- Menu對應一個Menu的格式 --><menu xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 我們分三種情況進行設定 --> <!-- Part 1 :普通情況,我們增加三個MenuItem,item對應MenuItem的格式。item中的android:id直接就是item的ID,即我們menu.add()中的第二個引數。--> <item android:id="@+id/c11_close" <!-- title為顯示的文字,即menu.add()中的第三個引數的第四個引數,可採用@string/xxx --> android:title="Close" <!-- orderInCategory表明擺放的順序,不一定從0還是計算,但必須大於等於0,數值小的位於前,如果數值一樣,在我們這個例子中3又兩個值,則安順序擺放,此相當於menu.add()中的第三個引數order。當然我們建議從0,1,2,3....這樣依次給出,並且與XML行文的順序一致。--> android:orderInCategory = "3" <!-- icon設定圖示,不言自喻 --> android:icon="@drawable/android_focused" /> <item android:id="@+id/c11_no_icon" android:orderInCategory = "2" android:title = "Sans Icon" /> <item android:id="@+id/c11_disabled" android:orderInCategory="4" android:enabled="false" android:title="Disabled" /> <!-- Part 2 :Group的情況,我們在Group中放入2個item,如果我們要顯示3.4的方式,可以增加group的引數android:checkableBehavior來設定,single表示radio box,all表示checkbox,none表示checkable=flase。group中的android:id就是Gourp_ID,即menu.add()中的第一個引數。在這個例子中,我們設定這個group不可視,如果需要顯示,程式碼為:menu.setGroupVisible(R.id.c11_other_stuff, true);--> <groupandroid:id="@+id/c11_other_stuff" <!-- Item由android:orderInCategory來設定item的順序,在Group中我們可以通過menuCategory來設定另一個category,裡面的順序和default Category是不方在一起比較,例如這裡麼我們給出0和5,如圖所示,在顯示完default Category,再顯示這個sendonary的內容。 --> android:menuCategory="secondary" android:checkableBehavior="single" android:visible="false" > <item android:id="@+id/c11_later" android:orderInCategory="0" android:title="2nd-To-Last" /> <item android:id="@+id/last" android:orderInCategory="5" android:title="Last" /> </group> <!-- Part 3 :子menu的設定,將在menuItem內部巢狀一個<Menu>,在這個例子中的子選單,試驗了快捷鍵的方式--> <itemandroid:id="@+id/c11_submenu" android:orderInCategory="3" android:title="A submenu" > <menu> <item android:id="@+id/c11_non_ghost" android:title="Non-Ghost" android:visible="true" android:alphabeticShortcut="n"/> <item android:id="@+id/c11_ghost" android:title="Ghost" android:visible="true" android:alphabeticShortcut="g" /> </menu> </item> <!-- end of Part 3 --></menu>