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中增加子菜單的item
submenu.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>