最近在做播放器的時候遇到一個問題,在屏幕方向改變之后需要切換播放器全屏/非全屏的時候,在重寫了onConfigurationChanged方法並在manifest.xml配置文件中添加
- android:screenOrientation="sensor"
- android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
之后,在屏幕方向改變之后確實切換了播放器的方向,但是在我的程序中,需要一個播放器控制按鈕,當用戶點擊按鈕時手動切換播放器方向(即播放器全屏/小屏狀態切換)和屏幕方向改變時自動切換兩個功能並存;最開始想的是直接使用setRequestedOrientation()設置屏幕方向應該就OK了,但是發現這樣做是行不通的.之后了解到因為setRequestedOrientation設置屏幕方向之后,比如說setRequestedOrientation(portrait)方法,就設定了屏幕方向是portrait,和在清單文件中配置android:screenOrientation="portrait"是同等的效果;也即不再響應屏幕方向改變,只支持portrait方向;
言歸正轉,說我的處理方法,android給我們提供了OrientationEventListener,從字面意思就知道是干什么用的;這個監聽器有一個onOrientationChanged(int rotation)方法會將當前屏幕旋轉的度數返回給用戶;
先看持接口中方法返回的旋轉度數的計算方法;
上圖中金色區域就是手機,角度就是綠線和紅線之間的角度,順時針旋轉手機,角度增大,角度范圍0-360;手機平放的角度為-1;
下面分別是橫屏和豎屏的界面,按鈕即用於切換屏幕方向;
再看看具體實現:
1.聲明變量
private OrientationEventListener mOrientationListener; // 屏幕方向改變監聽器 private boolean mIsLand = false; // 是否是橫屏 private boolean mClick = false; // 是否點擊 private boolean mClickLand = true; // 點擊進入橫屏 private boolean mClickPort = true; // 點擊進入豎屏
2.初始化監聽器
/** * 開啟監聽器 */ private final void startListener() { mOrientationListener = new OrientationEventListener(this) { @Override public void onOrientationChanged(int rotation) { // 設置豎屏 if (((rotation >= 0) && (rotation <= 30)) || (rotation >= 330)) { if (mClick) { if (mIsLand && !mClickLand) { return; } else { mClickPort = true; mClick = false; mIsLand = false; } } else { if (mIsLand) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); mIsLand = false; mClick = false; } } } // 設置橫屏 else if (((rotation >= 230) && (rotation <= 310))) { if (mClick) { if (!mIsLand && !mClickPort) { return; } else { mClickLand = true; mClick = false; mIsLand = true; } } else { if (!mIsLand) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); mIsLand = true; mClick = false; } } } } }; mOrientationListener.enable(); }
3.設置按鈕點擊切換屏幕方向響應事件
mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mClick = true; if (!mIsLand) { if (onClickOrientationListener != null) { onClickOrientationListener.landscape(); } setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); mIsLand = true; mClickLand = false; } else { if (onClickOrientationListener != null) { onClickOrientationListener.portrait(); } setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); mIsLand = false; mClickPort = false; } } });
4.上面用到一個接口OnClickOrientationListener,里面包含兩個方法,分別用於用戶點擊切換橫屏/豎屏時的回調;
interface OnClickOrientationListener { public void landscape(); public void portrait(); }
代碼貼完了, 簡單說說思路,點擊的時候,直接切換屏幕方向,切換之后,需要當手機屏幕也旋轉到所切換的方向之后,才又開始監聽手機屏幕旋轉事件,這樣就實現了setRequestedOrientation之后仍然可以通過旋轉手機切換屏幕的功能;
舉個例子:
-->手機當前是豎屏狀態,Activity也是豎屏狀態
-->用戶點擊切換按鈕
-->Activity切換為橫屏,手機為豎屏;此時通過設置flag,使OrientationListener監聽到豎屏時不再處理事件,waiting...
-->直到當用戶把手機旋轉為橫屏狀態之后,更改flag,使OrientationListener監聽到豎屏時處理相應的事件
-->當用戶再次旋轉手機切換為豎屏之后,Activity即可自動切換為豎屏;
橫屏點擊切換豎屏理論同上;
第三步,用戶點擊切換按鈕之后進入橫屏,此時就不響應監聽到的豎屏處理事件,並且要等待到第四步用戶把手機旋轉為橫屏狀態之后再響應豎屏監聽;這樣定義似乎不太合理,但從用戶的角度看,不可能用戶點擊了要進入橫屏,卻仍然把手機給豎屏方向拿着;
最后,當不需要監聽屏幕方向的時候,需要調用OrientationListener.disable()關閉監聽器;
小記錄一下相關知識01/07/2014
private int getScreenRotation() { WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); try { Method m = display.getClass().getDeclaredMethod("getRotation"); return (Integer) m.invoke(display); } catch (Exception e) { return Surface.ROTATION_0; } } private int getScreenOrientation() { switch (getScreenRotation()) { case Surface.ROTATION_0: return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; case Surface.ROTATION_90: return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; case Surface.ROTATION_180: return (Build.VERSION.SDK_INT >= 8 ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); case Surface.ROTATION_270: return (Build.VERSION.SDK_INT >= 8 ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); default: return 0; } }