安卓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可以自定義位置顯示選單,它也算小巧而靈活。
最後一點:不要被這篇部落格的滾動條給嚇到,裡面有很多程式碼、結果截圖,真正有用的程式碼少的可憐。( ̄︶ ̄)