Android ActionBar中Overflow Menu(溢位選單)中的一些問題

IT_xiao小巫發表於2015-04-01

前言

開始前我們先來關注一下Android Overflow menu的幾個相關問題:

  1. 什麼是Overflow menu
  2. Android 3.0以上預設不顯示overflow menu
  3. 如何強制在Android 4.4.4以下的手機顯示overflow menu
  4. 經測試,魅族手機無法強制顯示overflow menu,其他手機暫無發現這種問題

先來兩張對比圖:

左邊是紅米手機的效果,右邊是針對魅族Mx4pro做的適配效果。

什麼是overflow menu?

先來解釋一下什麼是overflow menu,中文叫做“溢位選單”,顧名思義溢位的選單,我們有些手機是有實體按鍵或虛擬按鍵,其中一個是選單鍵,有的可能沒有,例如魅族就只有一個back的返回虛擬鍵。點選選單鍵的時候,就會在底部彈出溢位選單,在Android 3.0之後,也就是API 11之後才可以加入ActionBar控制元件的,再其右上角會有三個點,那個就是我們說的overflow menu,我們在menu中定義選單項就可以彈出類似上面紅米手機彈出的效果。關於ActionBar有很多用法,本篇部落格就不展開講,有興趣的同學可以到官網進行學習。

如何在ActionBar強制顯示溢位選單?

Android 3.0以上的手機預設是不顯示溢位選單的,那如何強制在Android 4.4以下的手機顯示溢位選單呢?可以使用以下方法:

// 強制actionbar顯示overflow選單
	// force to show overflow menu in actionbar for android 4.4 below
	private void getOverflowMenu() {
		try {
			ViewConfiguration config = ViewConfiguration.get(this);
			Field menuKeyField = ViewConfiguration.class
					.getDeclaredField("sHasPermanentMenuKey");
			if (menuKeyField != null) {
				menuKeyField.setAccessible(true);
				menuKeyField.setBoolean(config, false);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

適配類似魅族手機無法顯示溢位選單的解決方案

上面已經解決了如何在Android 4.4以下的手機強制顯示溢位選單,下面來解決一些奇葩手機無法顯示溢位選單的問題。為什麼魅族手機無法顯示,瞭解到的是魅族沒有所謂的actionbar,它們稱為smartbar,看來是魅族的工程師把官方的actionbar進行了修改。一個字,坑!不過小巫想到了一個解決辦法,我們每部手機都有自己的手機品牌,我們可以針對這些奇葩手機進行適配,溢位選單我們就使用popupwindow來替代。

獲得手機資訊

/**
	 * 獲取IMEI號,IESI號,手機型號
	 */
	public static void getInfo(Context context) {
		TelephonyManager mTm = (TelephonyManager) context
				.getSystemService(Context.TELEPHONY_SERVICE);
		String imei = mTm.getDeviceId();
		String imsi = mTm.getSubscriberId();
		String mtype = android.os.Build.MODEL; // 手機型號
		String mtyb = android.os.Build.BRAND;// 手機品牌
		String numer = mTm.getLine1Number(); // 手機號碼,有的可得,有的不可得
		Log.i("text", "手機IMEI號:" + imei + "手機IESI號:" + imsi + "手機型號:" + mtype
				+ "手機品牌:" + mtyb + "手機號碼" + numer);
	}

獲取手機品牌

/**
	 * 得到手機品牌
	 * @return
	 */
	public static String getPhoneBrand() {
		return android.os.Build.BOARD;
	}

得到手機品牌之後,我們就在建立溢位選單的時候進行判斷:

1. 如果是魅族手機,則載入自定義選單
2. 如果不是,則使用系統的溢位選單,載入選單內容

@Override
	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
		// Inflate the menu; this adds items to the action bar if it is present.
		super.onCreateOptionsMenu(menu, inflater);
		String brand = PhoneUtils.getPhoneBrand();
		if ("mx4pro".equals(brand)) {
			inflater.inflate(R.menu.overflow_menu, menu);
		} else {
			inflater.inflate(R.menu.main, menu);
		}
	}

溢位選單檔案
/res/menu/main.xm

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

        <item
            android:id="@+id/menu_hotest"
            android:orderInCategory="100"
            android:showAsAction="never"
            android:title="@string/menu_hotest"/>
        <item
            android:id="@+id/menu_lastest"
            android:orderInCategory="100"
            android:showAsAction="never"
            android:title="@string/menu_lastest"/>

</menu>

自定義的選單檔案

/res/menu/overflow_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/menu_overflow"
        android:icon="@drawable/ic_more"
        android:orderInCategory="100"
        android:showAsAction="always"
        android:title="@string/menu_overflow">
    </item>

</menu>

自定義選單就直接給它設定一個actionbar的按鈕,圖示是那三個點

我們在選中選單的時候執行我們的業務邏輯

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menu_overflow:
			popupOverflowMenu();
			return true;
		case R.id.menu_lastest:
			type = "latest";
			break;
		case R.id.menu_hotest:
			type = "hotest";
			break;
		default:
			break;
		}
		return super.onOptionsItemSelected(item);
	}

如果是魅族mx4pro手機,就彈出我們的自定義選單,下面是實現:

/**
	 * 彈出自定義溢位選單
	 */
	public void popupOverflowMenu() {
		// 顯示溢位選單的時候設定背景半透明
		setWindowAlpha(0.5f); 
		// 獲取狀態列高度
		Rect frame = new Rect();
		getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
		// 狀態列高度 frame.top
		int xOffset = frame.top + getActivity().getActionBar().getHeight() - 25; // 減去陰影寬度,適配UI
		int yOffset = Dp2Dx(getActivity(), 8f); // 設定x方向offset為5dp
		View parentView = getActivity().getLayoutInflater().inflate(R.layout.fragment_portfolio, null);
		View popView = getActivity().getLayoutInflater().inflate(R.layout.action_overflow_menu, null);
		// popView即popupWindow佈局
		PopupWindow popupWindow = new PopupWindow(popView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
		// 必須設計BackgroundDrawable後setOutsideTouchable(true)才會有效。這裡在XML中定義背景,所以這裡為null
		popupWindow.setBackgroundDrawable(new ColorDrawable(0000000000));
		popupWindow.setFocusable(true);
		popupWindow.setOutsideTouchable(true); // 點選外部關閉
		popupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
		// 設定Gravity,讓它顯示在右上角
		popupWindow.showAtLocation(parentView, Gravity.RIGHT | Gravity.TOP, yOffset, xOffset);
		popupWindow.setOnDismissListener(new OnDismissListener() {

			@Override
			public void onDismiss() {
				// popupWindow消失時,設定為全透明
				setWindowAlpha(1f);
			}
		});
	}

	/**
	 * 設定螢幕透明度
	 * @param alpha
	 */
	private void setWindowAlpha(float alpha) {
		WindowManager.LayoutParams lp = getActivity().getWindow().getAttributes();  
		lp.alpha = alpha; //0.0-1.0  
		getActivity().getWindow().setAttributes(lp);
	}

	public int Dp2Dx(Context context, float dp) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dp * scale + 0.5f);
	}

這裡就是通過自定義pupupwindow,指定popupwindow的xml佈局,這個自己來定,根據父佈局來顯示popupwindow的位置,當popupwindow顯示的時候,設定螢幕為半透明,監聽它的消失事件,消失的時候,設定螢幕為全透明效果。最終實現適配魅族手機,這裡是小巫想到的一個解決方案,有更好的方法,可以留言交流一下。

相關文章