安卓menu的介紹與使用
選單之前是使用者點選系統的選單鍵才展示出來的,後來這個鍵漸漸被移除,選單變成了點選任意的view都可以展示。選單非為3種:
1. Options menu and action bar 選項選單和操作欄
2. Context menu and contextual action mode 上下文選單和上下文動作模式
3. Popup menu 彈出式選單
現在逐一介紹這3種菜單的使用方法:
1.Options menu
這個選單比較原始,它的實現必須通過點選actionbar 上的按鈕或手機自帶的選單鍵才能顯示。首先,在res檔案目錄下,新建資料夾menu,然後再menu資料夾中新建menu的xml檔案,這裡我的檔名為"option_menu".
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/add" android:icon="@mipmap/addition" android:title="新增"/> <item android:id="@+id/see" android:icon="@mipmap/eye" android:title="發現"/> <item android:id="@+id/state" android:icon="@mipmap/emoji" android:title="表情"/> </menu>
我自己在mipmap資料夾中放了3張40*40 的小圖示(你可以從圖示網站自己去下載),這個xml檔案比較簡單。接著我們在activity中把這個xml填充成view。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.option_menu,menu); return true; } }
這是點選actionbar右邊的按鈕彈出的介面截圖,但奇怪的是我在xml配置的圖示沒顯示出來 ̄︿ ̄。通過百度,原來是menu這個物件搞的鬼
我在這裡打斷點時可以發現menu在執行時實際上引用的是MenuBuilder物件。這個MenuBuilder物件和menu是什麼關係呢? 通過sdk查詢,他倆的關係是:
MenuBuilder------實現-----》SupportMenu(介面)-------繼承--------》Menu(介面)。而在MenuBuilder這個類中控制圖示顯示的方法是:
初始時,mOptionlIconsVisible = false,我們只要呼叫setOptionalIconsVisible(true),就能解決問題。操作執行時的menuBuilder物件,很容易讓我們想到用反射。。。我們寫個方法,通過MenuBuilder的class物件,來調我們setOptionalIconsVisible方法。
懂反射的語法,上面的方法應該很容易就能看懂了。最後看看效果:
大功告成!接下來就來監聽選單的點選事件了,方法是onOptionsItemSelected(),見名思意,這個方法和onCreateOptionsMenu()方法的關鍵字都是option,通過androidstudio強大的提示功能,也不用去記全名。
這裡我只寫了選單中其中一項的點選事件,其他的類似。提醒一點:這裡的switch語句不像通常那樣用break,而是用return true,有兩個原因:1.我們用break,最後還是要在switch語句結束後,返回布林值給方法,還不如在case 中直接返回。2:這一點更重要,方法要求返回布林結果就是為了消費這次點選事件,true就是消費,false不消費,如果不消費,那麼這個點選事件會繼續傳遞給activity裡的fragment。而我們這個簡單到什麼都沒有,所有返回true還是false,沒什麼影響,但建議return true來消費這次點選事件。
好了,最簡單的選項選單已經介紹完了,接下來看看更高階一點的上下文選單。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.Context menu
對於第一個optionmenu 只跟actionBar關係好的這個事實,讓開發人員不滿意:我一個介面這麼多元素難道就不能彈選單嗎?那麼上下文選單的出現就可以讓我們少些抱怨。上下文選單分為兩種:
(1) floating context menu 浮動上下文選單:它的效果是當你長按控制元件時,會在螢幕中央出現一個列表。類似於你長按qq訊息列表中的某一項,會彈出置頂、刪除等選項 。
先完成一個小目標,點選一個按鈕,彈出浮動上下文選單。
老規矩:定義個float_menu.xml。
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/top" android:icon="@mipmap/up" android:title="置頂" /> <item android:id="@+id/delete" android:icon="@mipmap/delete" android:title="刪除" /> </menu>
佈局中加一個按鈕:
這是activity中的程式碼:
長按按鈕,出現的結果如圖:
我的圖示怎麼又不見了!!!難道之前的那個setIconEnable()方法失靈了。真是到處是坑。我通過打斷點檢視menu這個物件,結果如圖:
看看之前這裡得到的是MenuBuilder物件,現在建立ContextMenu,就成了ContextMenuBuilder物件。我查了這個類,結果發現ContextMenuBuilder繼承MenuBuilder,那就好辦了,將setIconEnable()方法改一行就OK:
圈起來的就是獲取父類的class物件,來看看結果:
可愛的圖示終於又出現了。針對一個view彈出浮動選單,除了不要忘記對控制元件註冊上下文選單,整體而言,還算簡單。現在看看對listview 註冊上下文選單。別擔心,更上面的程式碼有90%是一樣的,不管怎麼,把它做出來,也很有成就感。
1.先把佈局中的按鈕換成listview:
2.這是activity更改的程式碼,另外的兩個方法 onCreateContextMenu()和 setIconEnable() 都未做更改:
3.當你長按列表中任意一項出現的結果如圖:
4.處理點選浮動選單事件:
注意:如果是普通的view,紅線那行是不需要的,其中info,position是長按項在list中的角標。
(2)contextual action mode 上下文操作模式,它會在螢幕頂部彈出 context action bar(簡稱CAB) .它的用處在於,你看到一段不錯的文字,先把他複製下來,你長按控制元件就會在頂部出現CAB,操作完後再關掉CAB,很方便。
我們基於上面的listview再做修改來展示出CAB。
1.修改onCreate()裡的程式碼:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = findViewById(R.id.listview); Random random = new Random(); for (int i=0;i<array.length;i++){ array[i] =String.valueOf(random.nextInt(1000)); } //就因為這行程式碼,使按鈕的長按事件與onCreateContextMenu建立了聯絡,所以非常重要 ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,array); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { Log.d(TAG,"onCreateActionMode"); MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.float_menu,menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { Log.d(TAG,"onPrepareActionMode"); return false; } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { //長按控制元件呼叫的第一個方法。 Log.d(TAG,"onItemCheckedStateChanged"); } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { Log.d(TAG,"onActionItemClicked"); switch (item.getItemId()){ case R.id.top: Toast.makeText(MainActivity.this, "置頂", Toast.LENGTH_SHORT).show(); mode.finish(); return true; default: return false; } } @Override public void onDestroyActionMode(ActionMode mode) { Log.d(TAG,"onDestroyActionMode"); } }); }
上面建立setMultiChoiceModeListener()的方法回撥順序,我特地進行了調整成了它回撥的順序,有前到後。5個回撥中,當你長按listview中的某一項是,最先回調的是前3個方法,點選點選了頭部CAB後,才會呼叫後兩個。其中mode.finisha()如果不加進去,那麼頭部欄是不會消失的。最終結果圖為:
這裡只有圖示,文字不見,我沒有追究了,哎!這種從最上面彈出的actionbar更高階些。它把我們從點選控制元件到彈出bar,再到點選選單項的全部過程用5個回撥,解析的很全面。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3.Popup menu 彈出式選單:
熟悉popopwindow 的情形下,使用popupmenu應該會覺得很容易。在預設情形下:popup menu 顯示在控制元件上面,如果空間不過,則在下方顯示,當前也可以自己自定義位置顯示。它的使用與第一種options menu99%相似。
將layout中的listview再改回button,這就不上截圖了,直接看acvity中的程式碼就一目瞭然。
public class MainActivity extends AppCompatActivity implements PopupMenu.OnMenuItemClickListener{ private static final String TAG = "MainActivity"; private Button showPopMenuBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); showPopMenuBtn = findViewById(R.id.showPopmenuBtn); showPopMenuBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PopupMenu popup = new PopupMenu(MainActivity.this,v); MenuInflater inflater = popup.getMenuInflater(); inflater.inflate(R.menu.option_menu,popup.getMenu()); setIconEnable(popup.getMenu(),true); popup.show();//這裡給popup設定監聽事件,而整個activity實現了監聽。 popup.setOnMenuItemClickListener(MainActivity.this); } }); } public void setIconEnable(Menu menu, boolean isVisible){ if (menu !=null){ try { Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible",boolean.class); method.setAccessible(true); method.invoke(menu,isVisible); } catch (Exception e) { e.printStackTrace(); } } } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()){ case R.id.add: Toast.makeText(MainActivity.this,"新增",Toast.LENGTH_SHORT).show(); return true; default: return false; } }
執行結果如圖:
從popup menu設計初衷上,也只是為了對單個控制元件選定進行了操作,跟上下文選單中的浮動上下文選單功能是一模一樣的,甚至官方推薦你用第二種。只不過由於popwindow自身的屬性,所以讓他在menu中也佔了一席之地。我自人以為,如果你想點個控制元件,讓它彈出選單,用第二種是最好的。其中第二種上下文選單中的浮動選單比較簡單,但可以滿足非常多的普通需求,當你需要更詳細的互動過程控制,就考慮上下文選單中第二種操作模式。由於popmenu可以自定義位置顯示選單,它也算小巧而靈活。
最後一點:不要被這篇部落格的滾動條給嚇到,裡面有很多程式碼、結果截圖,真正有用的程式碼少的可憐。( ̄︶ ̄)