菜單之前是用戶點擊系統的菜單鍵才展示出來的,后來這個鍵漸漸被移除,菜單變成了點擊任意的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 顯示在控件上面,如果空間不過,則在下方顯示,當前也可以通過設置setGravity()來設置它跟錨的gravity值。它的使用與第一種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中也占了一席之地。我自人以為,如果你想點個控件,讓它彈出菜單,用第二種是最好的。其中第二種上下文菜單中的浮動菜單比較簡單,但可以滿足非常多的普通需求,當你需要更詳細的交互過程控制,就考慮上下文菜單中第二種操作模式。
最后一點:不要被這篇博客的滾動條給嚇到,里面有很多代碼、結果截圖,真正有用的代碼少的可憐。( ̄︶ ̄)