Android開發之Menu:OptionMenu(選項選單)、ContextMenu(上下文選單)、SubMenu(子選單)

小雷FansUnion發表於2015-11-20


選單的概念,現在已經很普及了。
Windows系統、Mac、桌面版Linux、Java Swing等,都有視覺化選單。
一、Android平臺3種選單
  選項選單(OptionMenu)、上下文選單(ContextMenu)、子選單(SubMenu)。


1.Option Menu 
一般手機上都會提供Menu的按鈕,對應的就是這個選單彈出。 


主要步驟就是複寫Activity父類中的onCreateOptionMenu(Menu menu)方法,然後通過Menu的add方法來新增選單進去,
最後,當我們去點選某項的選項的時候,覆蓋重寫onOptionsItemSelected(MenuItem item)這個方法去實現點選事件 。 


2.Context Menu 
上下文選單,就是和當時的環境(使用者介面、某個流程)相關的選單。
比如對於檔案管理器來說,一些增刪改查就可以擺在ContextMenu中,它其實是實現了一個使用者長按點選後彈出的來一個選單。


3.SubMenu
  子選單,在概念上,子選單可以說是“屬於”以上型別的。
  
二、理解Android選單
  選單是許多應用程式不可或缺的一部分,Android中更是如此,所有搭載Android系統的手機甚至都要有一個"Menu"鍵,由此可見選單在Android程式中的特殊性。Android SDK提供的選單有如下幾種:


選項選單:最常規的選單,android中把它叫做option menu
子選單:android中點選子選單將彈出懸浮視窗顯示子選單項。子選單不支援巢狀,即子選單中不能再包括其他子選單。
上下文選單:android中長按檢視控制元件後出現的選單,windows點選右鍵彈出的選單即上下文選單
圖示選單:這個比較簡單,就是帶icon的選單項,需要注意的是子選單項、上下文選單項、擴充套件選單項均無法顯示圖示。
選擇選單(alternative menu):用的比較少,以後單獨介紹,本文先跳過(其實是我還沒弄明白啦o(≧v≦)o~~)
擴充套件選單:選項選單最多隻能顯示6個選單項,超過6個時,第6個選單項會被系統替換為一個叫“更多”的子選單,原來顯示不下的選單項都作為“更多”選單的子選單項。


Android SDK提供的選單有如下幾種:


選項選單:最常規的選單,android中把它叫做option menu
子選單:android中點選子選單將彈出懸浮視窗顯示子選單項。子選單不支援巢狀,即子選單中不能再包括其他子選單。
上下文選單:android中長按檢視控制元件後出現的選單,windows點選右鍵彈出的選單即上下文選單
圖示選單:這個比較簡單,就是帶icon的選單項,需要注意的是子選單項、上下文選單項、擴充套件選單項均無法顯示圖示。
選擇選單(alternative menu):用的比較少。(網上找了篇文章,沒有看懂,很陌生的感覺。)
擴充套件選單:選項選單最多隻能顯示6個選單項,超過6個時,第6個選單項會被系統替換為一個叫“更多”的子選單,原來顯示不下的選單項都作為“更多”選單的子選單項。


android.view.Menu介面代表一個選單,android用它來管理各種選單項。
注意我們一般不自己建立menu,因為每個Activity預設都自帶了一個,我們要做的是為它加選單項和響應選單項的點選事件。
android.view.MenuItem代表每個選單項,android.view.SubMenu代表子選單。其三者的關係可以用下圖來表示


每個activity包含一個選單,一個選單又能包含多個選單項和多個子選單,子選單其實也是選單(因為它實現了Menu介面),因此子選單也可以包含多個選單項。
SubMenu繼承了Menu的addSubMenu()方法,但呼叫時會丟擲執行時錯誤。
OnCreateOptionsMenu()和OnOptionsMenuSelected()是activity中提供了兩個回撥方法,用於建立選單項和響應選單項的點選。




三、響應OptionMenu的3種方式
1.重寫activity類的 onOptionsItemSelected(MenuItem)回撥方法,每當有選單項被點選時,android就會呼叫該方法,並傳入被點選選單項。
  publicboolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    //響應每個選單項(通過選單項的ID)
case1:
        // do something here
break;


    default:
        //對沒有處理的事件,交給父類來處理
return super.onOptionsItemSelected(item);
    }
    //返回true表示處理完選單項的事件,不需要將該事件繼續傳播下去了
return true;
}

  這個地方的“default”,我覺得比較好。



2.使用監聽器
雖然第一種方法是推薦使用的方法,android還是提供了類似java swing的監聽器方式來響應選單。使用監聽器的方式分為兩步:
//第一步:建立監聽器類
class MyMenuItemClickListener implements OnMenuItemClickListener {
    @Override
    publicboolean onMenuItemClick(MenuItem item) {
        // do something here...
returntrue;    //finish handling
    }
}


//第二步:為選單項註冊監聽器
menuItem.setOnMenuItemClickListener(new MyMenuItemClickListener());
3.使用Intent響應選單
第3種方式是直接在MenuItem上呼叫setIntent(Intent intent)方法,這樣android會自動在該選單被點選時呼叫 startActivity(Intent)。
但是個人認為與其這樣還不如直接在onOptionsItemSelected的case裡手動呼叫 startActivity(Intent)來的直觀。


個人比較傾向於第1種方式,如果選單事件程式碼比較多,可以考慮第2種方式。
第2種方式的問題是,可能需要傳遞引數到Listener。


四、圖示選單
  // 子選單項不支援顯示圖示,這樣做是沒意義的,儘管不會報錯!
    menuitem1.setIcon(R.drawable.displaysettings);
    
    //但是子選單本身是支援圖示的
    subMenu.setIcon(R.drawable.settings);
  選單的前面可以有個圖示,後文的程式碼中,不再具體演示用法。


五、Context Menu
上下文選單與Options Menu最大的不同在於,Options Menu的擁有者是Activity,而上下文選單的擁有者是Activity中的View。
每個Activity有且只有一個Options Menu,它為整個Activity服務。
而一個Activity往往有多個View,並不是每個View都有上下文選單,這就需要我們顯示地通過registerForContextMenu(View view)來指定。


儘管上下文選單的擁有者是View,生成上下文選單卻是通過Activity中的onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法,
該方法很像生成Options Menu的onCreateOptionsMenu(Menu menu)方法。
兩者的不同在於,onCreateOptionsMenu只在使用者第一次按“Menu”鍵時被呼叫,而onCreateContextMenu會在使用者每一次長按View時被呼叫,而且View必須已經註冊了上下文選單。


另一個值得注意的就是上圖中的ContextMenuInfo,該類的物件被傳入onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)方法,那麼它有什麼用呢?
有時候,檢視元素需要向上下文選單傳遞一些資訊,比如該View對應DB記錄的ID等,這就要使用ContextMenuInfo。
需要傳遞額外資訊的View需要重寫getContextMenuInfo()方法,返回一個帶有資料的ContextMenuInfo實現類物件。


如何建立和響應上下文選單的3個步驟


1.在activity的onCreate(...)方法中為一個view註冊上下文選單。


2.在onCreateContextMenuInfo(...)中生成上下文選單。


3.在onContextItemSelected(...)中響應上下文選單項。 


六、程式碼示例
需要說明的是,Option Menu,按左鍵選單或者右鍵選單按鈕,彈出選單。
Context Menu,長按View,彈出選單。


OptionMenu程式碼
package cn.fansunion.menu;


import cn.fansunion.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;


/**
 * ①:直接去覆蓋public boolean onCreateOptionsMenu(Menu menu) { code......},
 * 這個方法,需要注意的是,這個方法如果覆蓋的了,只會被建立一次,也就是說, 選項選單隻會去被例項化一次,之後就不會被去呼叫了
 * ②:呼叫Menu中的add()方法,來新增每一個選單選項, add(groupId, itemId, order, titleRes) group:
 * 選項組號,一般都設定成0就OK啦 itenId: 選項的Id 很重要 order:順序,一般來說都設定0就行了 titelRes: 選項的標題名字
 * ③:當我們去點選某項的選項的時候,覆蓋重寫onOptionsItemSelected(MenuItem item)這個方法去實現點選事件.
 */
public class OptionMenuActivity extends Activity {
	// 點選選單選項的常量Id
	private static final int MENU_ONE = 1;
	private static final int MENU_TWO = 2;
	private static final int MENU_THREE = 3;
	private int MENU_SUB = 11;
	private static final  int GROUP_ZERO = 0;
	
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.option_menu);


	}


	/**
	 * @param group
	 *            : 選項組號,一般都設定成0就OK啦
	 * @param itenId
	 *            : 選項的Id 很重要
	 * @param order
	 *            :順序,一般來說都設定0就行了
	 * @param titelRes
	 *            : 選項的標題名字
	 */
	public boolean onCreateOptionsMenu(Menu menu) {	
		menu.add(GROUP_ZERO, MENU_ONE, GROUP_ZERO, "小雷");
		// 一個menu可以包括多個子選單
		SubMenu subMenu=menu.addSubMenu(GROUP_ZERO, MENU_SUB, GROUP_ZERO, "小雷興趣");
		subMenu.add(GROUP_ZERO, MENU_TWO, GROUP_ZERO, "網際網路技術");
		subMenu.add(GROUP_ZERO, MENU_THREE, GROUP_ZERO, "投資理財");
		return true;


	}


	/**
	 * 
	 * @param item
	 *            .getItemId() 獲取被點選的Id
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case MENU_ONE:
			Toast.makeText(this, "你點選了Menu1", Toast.LENGTH_LONG).show();
			break;
		case MENU_TWO:
			Toast.makeText(this, "你點選了Menu2", Toast.LENGTH_LONG).show();
			break;
		case MENU_THREE:
			Toast.makeText(this, "你點選了Menu3", Toast.LENGTH_LONG).show();
			break;
		
		default:
			return super.onOptionsItemSelected(item);
		}
		return true;
	}
}


佈局檔案layout
option_menu.xml
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    >  
    <TextView    
       android:layout_width="fill_parent"   
       android:layout_height="wrap_content"   
       android:text="FansUnion,Android Demo "/>  
   
</LinearLayout>  



ContextMenu程式碼
package cn.fansunion.menu;
import cn.fansunion.R;
import android.app.Activity;  
import android.os.Bundle;  
import android.view.ContextMenu;  
import android.view.ContextMenu.ContextMenuInfo;  
import android.view.MenuItem;  
import android.view.View;  
import android.widget.TextView;  
import android.widget.Toast;  
  
public class ContextMenuActivity extends Activity {  
    private static final int GROUP = 0;
	private static final int MENU_ONE = 1;  
    private static final int MENU_TWO = 2;  
    private static final int MENU_THREE = 3;  
  
    private TextView contextView;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.context_menu);  
        contextView = (TextView) this.findViewById(R.id.contextMenu);  
        registerForContextMenu(contextView);  
    }  
  
    @Override  
    public void onCreateContextMenu(ContextMenu menu, View v,  
            ContextMenuInfo menuInfo) {  
        super.onCreateContextMenu(menu, v, menuInfo);  
        menu.add(GROUP, MENU_ONE, 0, "小雷FansUnion");  
        menu.add(GROUP, MENU_TWO, 0, "網際網路技術");  
        menu.add(GROUP, MENU_THREE, 0, "投資理財");  
    }  
  
    @Override  
    public boolean onContextItemSelected(MenuItem item) {  
        switch (item.getItemId()) {  
        case MENU_ONE:  
            Toast.makeText(this, "你 點選的是Menu1", Toast.LENGTH_LONG).show();  
            contextView.setText("你點選的是Menu1");  
            break;  
  
        case MENU_TWO:  
            Toast.makeText(this, "你點選的是Menu2", Toast.LENGTH_LONG).show();  
            contextView.setText("你點選的是Menu2");  
            break;  
        case MENU_THREE:  
            Toast.makeText(this, "你點選的是Menu3", Toast.LENGTH_LONG).show();  
            contextView.setText("你點選的是Menu3");  
            break;  
        }  
        return true;  
    }  
}  




佈局layout
context_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	 <TextView    
	   android:id="@+id/contextMenu"  
       android:layout_width="fill_parent"   
       android:layout_height="wrap_content"   
       android:text="FansUnion,ContextMenuDemo "/>  
</LinearLayout>	


  注:本文主要是根據以下3個地方的參考資料,簡化和優化了文字和程式碼,改造而來的。
  
參考資料  
  http://www.cnblogs.com/codingmyworld/archive/2011/08/21/2147829.html(5篇很不錯,最佳參考資料)
  http://nullpointsun.iteye.com/blog/1589850(中規中矩,1篇長的)
  http://www.linuxidc.com/Linux/2011-12/49593.htm(馬馬虎虎,3篇短的)

相關文章