開發時遇到播放視頻要用重力感應來橫豎屏,并且要有按鈕點擊橫豎屏,查了很多資料,并參考了網上一些實現思路,終于把重力感應橫豎屏完整實現,現封裝成工具類,可以直接使用。
一、實現原理
既然是重力感應橫豎屏,那就肯定要有重力感應,重力感應屬于傳感器的一類,所以需要用到重力感應傳感器,不清楚的同學可以google一下。
由于視頻播放或者直播頁面一直都需要重力感應,所以還需要綁定播放頁面的生命周期來注冊重力感應和注銷重力感應。這樣當注冊重力感應后,就可以獲取當前屏幕的朝向角度,根據這些角度來判斷哪個范圍需要橫豎屏。
二、實現邏輯
2.1 打造工具類ScreenRotateUtils
由于要綁定生命周期,并且可以點擊橫豎屏,所以可以做一個工具類,提供start()和stop方法來綁定生命周期,并且暴露一個點擊切換橫豎屏的方法toggleRotate()就可以。
-
start方法注冊監聽
public void start(Activity activity) { // 接收activity,用于操作屏幕的旋轉 mActivity = activity; // 注冊傳感器監聽 sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI); }
-
stop()方法注銷監聽
public void stop() { // 注銷監聽 sm.unregisterListener(listener); // 防止內存泄漏 mActivity = null; }
-
toggleRotate()方法,自動切換橫豎屏
public void toggleRotate() { /** * 先判斷是否已經開啟了重力感應,沒開啟就直接普通的切換橫豎屏 */ if(isEffetSysSetting){ try { int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION); // 如果用戶禁用掉了重力感應就直接切換 if (isRotate == 0) { changeOrientation(isLandscape, true); return; } } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } } /** * 如果開啟了重力感應就需要修改狀態 */ isOpenSensor = false; isClickFullScreen = true; if (isChangeOrientation) { changeOrientation(isLandscape, false); } else { isLandscape = !isLandscape; changeOrientation(isLandscape, false); } }
2.2 重力感應傳感器
Android提供了多種傳感器,這里只是簡單介紹一下重力感應傳感器,想了解更多請傳送。
-
初始化傳感器
// 這里在構造里初始化重力重力感應 private ScreenRotateUtil(Context context) { // 獲取傳感器管理器 sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); // 獲取傳感器類型 sensor = sm.getDefaultSensor(Sensor.TYPE_GRAVITY); // 初始化監聽器 listener = new OrientationSensorListener(mHandler); }
start()方法注冊監聽,stop()方法注銷監聽
-
看一下監聽里非常重要的方法onSensorChanged,當注冊了監聽后,這個方法會不停的回調用,不停的給Handler發送消息,然后就可以在Handler里修改屏幕的朝向。
public void onSensorChanged(SensorEvent event) { float[] values = event.values; int orientation = ORIENTATION_UNKNOWN; float X = -values[_DATA_X]; float Y = -values[_DATA_Y]; float Z = -values[_DATA_Z]; float magnitude = X * X + Y * Y; // Don't trust the angle if the magnitude is small compared to the y // value if (magnitude * 4 >= Z * Z) { // 屏幕旋轉時 float OneEightyOverPi = 57.29577957855f; float angle = (float) Math.atan2(-Y, X) * OneEightyOverPi; orientation = 90 - Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; } while (orientation < 0) { orientation += 360; } } /** * 獲取手機系統的重力感應開關設置,這段代碼看需求,不要就刪除 * screenchange = 1 表示開啟,screenchange = 0 表示禁用 * 要是禁用了就直接返回 */ if(isEffetSysSetting){ try { int isRotate = Settings.System.getInt(mActivity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION); // 如果用戶禁用掉了重力感應就直接return if (isRotate == 0) return; } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } } // 只有點了按鈕時才需要根據當前的狀態來更新狀態 if (isClickFullScreen) { if (isLandscape && screenIsPortrait(orientation)) { // 之前是橫屏,并且當前是豎屏的狀態 Log.d(TAG, "onSensorChanged: 橫屏 ----> 豎屏"); updateState(false, false, true, true); } else if (!isLandscape && screenIsLandscape(orientation)) { // 之前是豎屏,并且當前是橫屏的狀態 Log.d(TAG, "onSensorChanged: 豎屏 ----> 橫屏"); updateState(true, false, true, true); } else if (isLandscape && screenIsLandscape(orientation)) { // 之前是橫屏,現在還是橫屏的狀態 Log.d(TAG, "onSensorChanged: 橫屏 ----> 橫屏"); isChangeOrientation = false; } else if (!isLandscape && screenIsPortrait(orientation)) { // 之前是豎屏,現在還是豎屏的狀態 Log.d(TAG, "onSensorChanged: 豎屏 ----> 豎屏"); isChangeOrientation = false; } } // 判斷是否要進行中斷信息傳遞 if (!isOpenSensor) { return; } if (rotateHandler != null) { rotateHandler.obtainMessage(888, orientation, 0).sendToTarget(); } }
2.3 橫豎屏處理
-
這里的橫豎屏是去根據當前手機的朝向即orientation的角度進行判斷,然后調用mActivity.setRequestedOrientation()方法設置橫豎屏,這里進行了360°的判斷,實現了四個方向的橫豎屏。
if (msg.what == 888) { int orientation = msg.arg1; /** * 根據手機屏幕的朝向角度,來設置內容的橫豎屏,并且記錄狀態 */ if (orientation > 45 && orientation < 135) { mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); isLandscape = true; } else if (orientation > 135 && orientation < 225) { mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT); isLandscape = false; } else if (orientation > 225 && orientation < 315) { mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); isLandscape = true; } else if ((orientation > 315 && orientation < 360) || (orientation > 0 && orientation < 45)) { mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); isLandscape = false; } }
三、使用教程
- Activity的onResume()方法調用start()方法進行注冊監聽
- Activity的onPause()方法調用stop()方法注銷監聽
- 點擊全屏按鈕時調用toggleRotate()自動切換橫豎屏
- 如果需要手機系統的橫豎屏按鈕生效則調用setEffectSysSetting(true)