前言
本篇博客講解一下菜單Menu的使用。菜單在windows應用中使用十分廣泛,幾乎所有的windows應用都有菜單,Android中也加入了菜單的支持。從官方文檔了解到,從Android3.0(API level 11)開始,Android設備不再要求提供一個專門的菜單按鈕,轉而推薦使用ActionBar。所以現在市面上很多新設備使用三個虛擬按鍵,並不再額外提供菜單按鈕,但是按鈕的使用也是有些地方可以借鑒的。
因為Android版本的發展,對於菜單的支持各個版本有很大的區別,而Android3.0是個分水嶺,大概可以分為下面三類:
- OptionMenu和ActionBar:一些操作的集合,如果開發的平台在Android3.0之上,推薦使用ActionBar,如果開發的平台在Android2.3或之下,還是可以使用OptionMenu的。
- ContextMenu和ActionMode:ContextMenu是一個浮動的窗口形式展現一個選項列表,ActionMode是一個顯示在屏幕頂部的操作欄,允許用戶選擇多個選項,ActionMode在Android3.0之后才有支持。
- Pupop Menu:PopupMenu是固定在View上的模態菜單,以彈出的方式顯示,在Android3.0之后才有支持。
在XML中定義一個菜單
Android提供了標准的XML格式的資源文件來定義菜單項,並且對所有菜單類型都支持,推薦使用XML資源文件來定義菜單,之后再把它Inflater到Activity或者Fragment中,而不是在Activity中使用代碼聲明。
而菜單的XML資源文件,需要創建在/res/menu/目錄下,並且包含一下幾個元素:
- <menu>:定義一個Menu,是一個菜單資源文件的根節點,里面可以包含一個或者多個<item>和<group>元素。
- <item>:創建一個MenuItem,代表了菜單中一個選項。
- <group>:對菜單項進行分組,可以以組的形式操作菜單項。
<item>元素除了常規的id、icon、title屬性的支持,還有一個重要的屬性:android:showAsAction,這個屬性是起兼容性的,描述了在Android的高版本中,菜單項何時以何種方式加入到ActionBar中。
<group>是對菜單進行分組,分組后的菜單顯示效果並沒有區別,唯一的區別在於可以針對菜單組進行操作,這樣對於分類的菜單項,操作起來更方便,提供如下的操作:
- Menu.setGroupCheckable():菜單組內的菜單是否都可選。
- Menu.setGroupVisible():是否隱藏菜單組的所有菜單。
- Menu.setGroupEnabled():菜單組的菜單是否有用。
如果菜單項需要單選或者多選,可以使用android:checkableBehavior屬性設置,它可以對單個<item>或者<group>設置一個組,這個屬性接受三個參數:single,單選;all,多選,none,沒有Checked的選項,默認。
當創建好一個XML菜單資源文件之后,可以使用MenuInflater.inflate()方法填充菜單資源,使XML資源變成一個可編程的對象。
OptionMenu
OptionMenu,選項菜單,必須設備具有菜單按鈕才可以觸發。因為屏幕的限制,最多只能展示六個菜單項,如果定義的菜單項超出了六個,其他的菜單項將被隱藏,第六個菜單將會顯示“更多”,點擊展開更多的菜單。雖說在Android3.0之后不再推薦使用選項菜單,但是如果使用了,在Android3.0之后的設備上,選項菜單項將被默認轉移到ActionBar中,這個可以通過android:showAsAction屬性控制。
使用OptionMenu需要在Activity或者Fragment中重寫onCreateOptionsMenu(Menu)方法,在這個方法中聲明一個選項菜單。菜單的存在是為了提供操作,所以Activity和Fragment中還提供了一個onOptionsItemSelected(MenuItem)方法,用於響應選項菜單中選中的時候的響應。OptionMenu就是操作一個Menu對象和MenuItem對象。
下面通過兩個Demo來展示一段選項菜單的使用,分別使用代碼聲明菜單和XML資源文件聲明菜單的方式說明。
聲明代碼:
1 package com.example.menudemo; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.Menu; 7 import android.view.MenuItem; 8 import android.view.SubMenu; 9 import android.widget.Toast; 10 11 public class OptionMenu1Activitty extends Activity { 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 // TODO Auto-generated method stub 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_optionmenu1); 18 } 19 20 @Override 21 public boolean onCreateOptionsMenu(Menu menu) { 22 23 //直接Add菜單選項到Menu中 24 menu.add(1000, 100, 0, "System menu"); 25 //獲取添加的菜單選項,然后設置其圖標 26 MenuItem menuItem2=menu.add(1000, 101, 1, "User menu"); 27 menuItem2.setIcon(R.drawable.ic_launcher); 28 //獲取添加的菜單選項,增加一個Intent,點擊后轉向IntentActivity 29 MenuItem menuItem3=menu.add(1000, 102, 2, "Intent menu"); 30 menuItem3.setIcon(R.drawable.ic_launcher); 31 Intent intent=new Intent(OptionMenu1Activitty.this, IntentActivity.class); 32 menuItem3.setIntent(intent); 33 34 //添加一個SubMenu,點擊后彈出一個子菜單對話框 35 SubMenu submenu=menu.addSubMenu(1000, 103, 3, "Sub menus"); 36 submenu.add(1000, 104, 4, "Sub ment1"); 37 submenu.add(1000, 105, 4, "Sub ment2"); 38 submenu.add(1000, 106, 4, "Sub ment3"); 39 return true; 40 } 41 42 @Override 43 public boolean onOptionsItemSelected(MenuItem item) { 44 boolean flag; 45 switch (item.getItemId()) { 46 case 100: 47 Toast.makeText(OptionMenu1Activitty.this, "selected System menu", Toast.LENGTH_SHORT).show(); 48 flag=true; 49 break; 50 case 101: 51 Toast.makeText(OptionMenu1Activitty.this, "selected User menu", Toast.LENGTH_SHORT).show(); 52 flag=true; 53 break; 54 case 104: 55 Toast.makeText(OptionMenu1Activitty.this, "selected Sub menu1", Toast.LENGTH_SHORT).show(); 56 flag=true; 57 default: 58 flag=super.onOptionsItemSelected(item); 59 break; 60 } 61 return flag; 62 } 63 64 }
實現效果,Android2.3:
使用XML資源文件定義選項菜單,XML資源文件:
1 <?xml version="1.0" encoding="utf-8"?> 2 <menu xmlns:android="http://schemas.android.com/apk/res/android" > 3 4 <item 5 android:id="@+id/item1" 6 android:showAsAction="never" 7 android:title="System menu"> 8 </item> 9 <item 10 android:id="@+id/item2" 11 android:showAsAction="never" 12 android:title="User menu" 13 android:icon="@drawable/ic_launcher"> 14 </item> 15 <item 16 android:id="@+id/item3" 17 android:showAsAction="never" 18 android:title="Intent menu" 19 android:icon="@drawable/ic_launcher"> 20 </item> 21 <group android:id="@+id/group_file" > 22 <item android:id="@+id/menu_save" 23 android:title="menu group save" /> 24 <item android:id="@+id/menu_delete" 25 android:title="menu group delete" /> 26 </group> 27 <item android:id="@+id/file" 28 android:title="Sub menus" > 29 <!-- "file" submenu --> 30 <menu> 31 <item android:id="@+id/sub_menu1" 32 android:title="Sub menu1" /> 33 <item android:id="@+id/sub_menu21" 34 android:title="Sub menu2" /> 35 <item android:id="@+id/sub_menu3" 36 android:title="Sub menu3" /> 37 </menu> 38 </item> 39 </menu>
Java代碼:
1 package com.example.menudemo; 2 3 import android.app.Activity; 4 import android.content.Intent; 5 import android.os.Bundle; 6 import android.view.Menu; 7 import android.view.MenuItem; 8 import android.widget.TextView; 9 import android.widget.Toast; 10 11 public class OptionMenu2Activitty extends Activity { 12 private TextView tv; 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 // TODO Auto-generated method stub 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_optionmenu1); 18 tv=(TextView)findViewById(R.id.tvOptionMenu1); 19 tv.setText("加載XML資源填充Menu"); 20 } 21 22 @Override 23 public boolean onCreateOptionsMenu(Menu menu) { 24 // 使用布局文件加載菜單 25 getMenuInflater().inflate(R.menu.optionmenu2, menu); 26 return super.onCreateOptionsMenu(menu); 27 } 28 29 @Override 30 public boolean onOptionsItemSelected(MenuItem item) { 31 32 switch (item.getItemId()) { 33 case R.id.item1: 34 Toast.makeText(OptionMenu2Activitty.this, "selected System menu", Toast.LENGTH_SHORT).show(); 35 return true; 36 case R.id.item2: 37 Toast.makeText(OptionMenu2Activitty.this, "selected User menu", Toast.LENGTH_SHORT).show(); 38 return true; 39 case R.id.item3: 40 Intent intent=new Intent(OptionMenu2Activitty.this, IntentActivity.class); 41 startActivity(intent); 42 return true; 43 case R.id.menu_save: 44 Toast.makeText(OptionMenu2Activitty.this, "file save", Toast.LENGTH_SHORT).show(); 45 return true; 46 case R.id.sub_menu1: 47 Toast.makeText(OptionMenu2Activitty.this, "Selected sub_menu1", Toast.LENGTH_SHORT).show(); 48 return true; 49 default: 50 return super.onOptionsItemSelected(item); 51 } 52 53 54 } 55 56 }
效果和使用Java代碼聲明菜單一樣,這里就不再展示了。
ContextMenu
ContextMenu,上下文菜單提供了注冊在View組件的菜單操作,它以一個浮動的窗口顯示(類似於對話框),當用戶長按某被注冊了上下文菜單的視圖,則觸發上下文菜單顯示。通常都用於ListView或者GridView等視圖集合中。
使用上下文菜單的步驟:
- 使用Activity.registerForContextMenu(View)方法為指定View注冊上下文菜單。
- 在Activity或者Fragment中重寫onCreateContextMenu()方法,當被注冊的視圖接受到長按事件后,系統調用onCreateContextMenu()方法,在這個方法中聲明上下文菜單。
- 實現onContextItemSelected()方法,用於響應菜單想的選中。
示例,菜單XML資源文件代碼:
1 <menu xmlns:android="http://schemas.android.com/apk/res/android" > 2 3 <item 4 android:id="@+id/context_copy" 5 android:orderInCategory="100" 6 android:showAsAction="never" 7 android:title="Copy"/> 8 <item 9 android:id="@+id/context_edit" 10 android:orderInCategory="100" 11 android:showAsAction="never" 12 android:title="Edit"/> 13 <item 14 android:id="@+id/context_delete" 15 android:orderInCategory="100" 16 android:showAsAction="never" 17 android:title="Delete"/> 18 </menu>
Java代碼:
1 package com.example.menudemo; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.app.Activity; 7 import android.os.Bundle; 8 import android.view.ContextMenu; 9 import android.view.MenuInflater; 10 import android.view.MenuItem; 11 import android.view.View; 12 import android.view.ContextMenu.ContextMenuInfo; 13 import android.widget.AdapterView.AdapterContextMenuInfo; 14 import android.widget.ArrayAdapter; 15 import android.widget.ListView; 16 import android.widget.Toast; 17 18 public class ContextMenu1 extends Activity { 19 private ListView listview; 20 private List<String> dataList; 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.activity_contextmenu1); 25 listview=(ListView)findViewById(R.id.listView1); 26 dataList=getData(); 27 ArrayAdapter<String> adapter=new ArrayAdapter<String>(ContextMenu1.this,android.R.layout.simple_list_item_1, dataList); 28 listview.setAdapter(adapter); 29 //為ListView注冊上下文菜單 30 registerForContextMenu(listview); 31 } 32 33 @Override 34 public void onCreateContextMenu(ContextMenu menu, View v, 35 ContextMenuInfo menuInfo) { 36 super.onCreateContextMenu(menu, v, menuInfo); 37 //填充一個XML菜單文件 38 MenuInflater inflater = getMenuInflater(); 39 inflater.inflate(R.menu.contextmenu, menu); 40 } 41 42 @Override 43 public boolean onContextItemSelected(MenuItem item) { 44 //獲取上下文菜單綁定的AdapterView的額外信息 45 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); 46 switch (item.getItemId()) { 47 case R.id.context_copy: 48 Toast.makeText(ContextMenu1.this, "copy "+dataList.get(info.position), Toast.LENGTH_SHORT).show(); 49 return true; 50 case R.id.context_delete: 51 Toast.makeText(ContextMenu1.this, "delete "+dataList.get(info.position), Toast.LENGTH_SHORT).show(); 52 return true; 53 case R.id.context_edit: 54 Toast.makeText(ContextMenu1.this, "edit " +dataList.get(info.position), Toast.LENGTH_SHORT).show(); 55 return true; 56 default: 57 return super.onContextItemSelected(item); 58 } 59 } 60 //ListView數據 61 public List<String> getData() 62 { 63 List<String> data=new ArrayList<String>(); 64 for(int i=0;i<8;i++) 65 { 66 data.add("item"+i); 67 } 68 return data; 69 } 70 }
效果展示,Android4.0:
ActionMode
ActionMode,是一個系統實現的用戶交互,當用戶使用ActionMode后,選擇一個選項,一個上下文操作欄會出現在屏幕的頂端,呈現出用戶可以對當前選中項目進行的操作選項。進入這種狀態可以通過后退按鈕或者調用finish()退出。ActionMode為Android3.0之后的支持,所以在開發3.0之后的應用,推薦使用ActionMode,而不是ContextMenu。
使用ActionMode的步驟:
- 實現ActionMode.Callback接口。在它的回調方法中,可以設置操作的上下文操作欄。
- 在需要顯示上下文操作欄的時候,調用startActionMode(ActionMode.Callback)。
ActionMode.Callback是ActionMode定義的一個內部接口,這個接口需要實現下面四個方法:
- boolean onCreateActionMode(ActionMode mode,Menu menu):第一次被創建的時候調用。
- boolean onPrepareActionMode(ActionMode mode,Menu menu):刷新菜單列表的時候被調用,一般使用false即可。
- boolean onActionItemClicked(ActionMode mode,MenuItem item):菜單項被選中的時候被調用。
- void onDestroyActionMode(ActionMode mode):退出或銷毀的時候被調用。
示例:
1 package com.example.menudemo; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import android.annotation.SuppressLint; 7 import android.app.Activity; 8 import android.os.Bundle; 9 import android.view.ActionMode; 10 import android.view.ActionMode.Callback; 11 import android.view.Menu; 12 import android.view.MenuInflater; 13 import android.view.MenuItem; 14 import android.view.View; 15 import android.widget.AdapterView; 16 import android.widget.AdapterView.AdapterContextMenuInfo; 17 import android.widget.AdapterView.OnItemLongClickListener; 18 import android.widget.ArrayAdapter; 19 import android.widget.ListView; 20 import android.widget.Toast; 21 22 public class ActionModeMenu1 extends Activity { 23 private ListView listview; 24 private List<String> dataList; 25 private ActionMode mActionMode; 26 @Override 27 protected void onCreate(Bundle savedInstanceState) { 28 // TODO Auto-generated method stub 29 super.onCreate(savedInstanceState); 30 setContentView(R.layout.activity_contextmenu1); 31 listview=(ListView)findViewById(R.id.listView1); 32 dataList=getData(); 33 ArrayAdapter<String> adapter=new ArrayAdapter<String>(ActionModeMenu1.this,android.R.layout.simple_list_item_1, dataList); 34 listview.setAdapter(adapter); 35 listview.setOnItemLongClickListener(new OnItemLongClickListener() { 36 @SuppressLint("NewApi") 37 @Override 38 public boolean onItemLongClick(AdapterView<?> parent, View view, 39 int position, long id) { 40 if (mActionMode != null) { 41 return false; 42 } 43 //顯示ActionMode 44 mActionMode = startActionMode(mActionModeCallback); 45 //標記選中項的下表 46 mActionMode.setTag(position); 47 //標記ListView為可選狀態 48 view.setSelected(true); 49 return true; 50 } 51 }); 52 } 53 public List<String> getData() 54 { 55 List<String> data=new ArrayList<String>(); 56 for(int i=0;i<8;i++) 57 { 58 data.add("item"+i); 59 } 60 return data; 61 } 62 63 private ActionMode.Callback mActionModeCallback=new Callback() { 64 65 @Override 66 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 67 //刷新菜單列表的時候被調用,但是一般無需刷新 68 return false; 69 } 70 71 @Override 72 public void onDestroyActionMode(ActionMode mode) { 73 //銷毀ActionMode 74 mActionMode = null; 75 } 76 77 @Override 78 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 79 //創建ActionMode 80 //使用資源文件填充 81 MenuInflater inflater = mode.getMenuInflater(); 82 inflater.inflate(R.menu.contextmenu, menu); 83 return true; 84 } 85 86 @Override 87 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 88 //獲取選項中下表 89 int position=(Integer)mode.getTag(); 90 switch (item.getItemId()) { 91 case R.id.context_copy: 92 Toast.makeText(ActionModeMenu1.this, "copy "+dataList.get(position), Toast.LENGTH_SHORT).show(); 93 //finish退出ActionMode模式 94 mode.finish(); 95 return true; 96 case R.id.context_delete: 97 Toast.makeText(ActionModeMenu1.this, "delete "+dataList.get(position), Toast.LENGTH_SHORT).show(); 98 mode.finish(); 99 return true; 100 case R.id.context_edit: 101 Toast.makeText(ActionModeMenu1.this, "edit " +dataList.get(position), Toast.LENGTH_SHORT).show(); 102 mode.finish(); 103 return true; 104 default: 105 return false; 106 } 107 } 108 }; 109 110 111 }
效果展示,Android4.0:
PopupMenu
PopupMenu,彈出菜單,一個模態形式展示的彈出風格的菜單,綁在在某個View上,一般出現在被綁定的View的下方(如果下方有空間)。
使用PopupMenu的步驟:
- 通過PopupMenu的構造函數實例化一個PopupMenu對象,需要傳遞一個當前上下文對象以及綁定的View。
- 調用PopupMenu.setOnMenuItemClickListener()設置一個PopupMenu選項的選中事件。
- 使用MenuInflater.inflate()方法加載一個XML文件到PopupMenu.getMenu()中。
- 在需要的時候調用PopupMenu.show()方法顯示。
示例:
1 public void showPopup(View v){ 2 PopupMenu popup=new PopupMenu(MainActivity.this, v); 3 popup.setOnMenuItemClickListener(new OnMenuItemClickListener() { 4 5 @Override 6 public boolean onMenuItemClick(MenuItem item) { 7 switch (item.getItemId()) { 8 case R.id.context_copy: 9 Toast.makeText(MainActivity.this, "select copy ", Toast.LENGTH_SHORT).show(); 10 return true; 11 case R.id.context_delete: 12 Toast.makeText(MainActivity.this, " select delete ", Toast.LENGTH_SHORT).show(); 13 return true; 14 case R.id.context_edit: 15 Toast.makeText(MainActivity.this, " select edit ", Toast.LENGTH_SHORT).show(); 16 return true; 17 default : 18 return false; 19 } 20 } 21 }); 22 popup.getMenuInflater().inflate(R.menu.contextmenu,popup.getMenu()); 23 popup.show(); 24 }
效果展示,Android4.0:
總結
以上就講解了Android下Menu的使用。因為上面的所有示例均在一個項目中完成的,所以有些低版本的操作,需要更改AndroidManifest.xml文件中的最低支持版本,改到8即可。在現在的實際開發中,最好還是使用對高版本只是的ActionBar、ActionMode、PopupMenu比較好。