1. 程式人生 > >Android 學習筆記(二七:Menu

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>