日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Android實現屏幕旋轉四個方向準確監聽_Android

作者:小風666 ? 更新時間: 2022-09-13 編程語言

在做相機開發時,遇到一個問題,就是需要監聽屏幕旋轉。最簡單的就是使用onConfigurationChanged()和OrientationEventListener這兩種方法來實現,但是最后都遇到了問題。

#1 一開始是使用onConfigurationChanged()這個回調,重新Activity里面的這個方法就可以了,簡單又方便。用了之后發現,它只能監聽,橫屏切豎屏的情況。左橫屏切右橫屏是監聽不到的,而且切完之后你也不知道是左橫屏還是右橫屏。下面是使用onConfigurationChanged()進行監聽的簡單使用。

@Override
? ? public void onConfigurationChanged(Configuration newConfig) {
? ? ? ? super.onConfigurationChanged(newConfig);
? ? ? ? if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
? ? ? ? ? ? // 橫屏
? ? ? ? }else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
? ? ? ? ? ? // 豎屏
? ? ? ? }
? ? }

#2 之后又想到了OrientationEventListener來監聽屏幕旋轉的實時角度,這個非常靈活,手機轉動實時角度都會回調出來。下面是使用OrientationEventListener的簡單實現。在適當的位置調用enable()和disable()來開啟和關閉監聽。

class MyOrientationEventListener extends OrientationEventListener {
?
? ? ? ? private static final int SENSOR_ANGLE = 10;
?
? ? ? ? public MyOrientationEventListener(Context context) {
? ? ? ? ? ? super(context);
? ? ? ? }
?
? ? ? ? @Override
? ? ? ? public void onOrientationChanged(int orientation) {
? ? ? ? ? ? Log.d(TAG, "onOrientationChanged orientation=" + orientation);
? ? ? ? ? ? if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
? ? ? ? ? ? ? ? return; ?//手機平放時,檢測不到有效的角度
? ? ? ? ? ? }
?
? ? ? ? ? ? //下面是手機旋轉準確角度與四個方向角度(0 90 180 270)的轉換
? ? ? ? ? ? if (orientation > 360 - SENSOR_ANGLE || orientation < SENSOR_ANGLE) {
? ? ? ? ? ? ? ? orientation = 0;
? ? ? ? ? ? } else if (orientation > 90 - SENSOR_ANGLE && orientation < 90 + SENSOR_ANGLE) {
? ? ? ? ? ? ? ? orientation = 90;
? ? ? ? ? ? } else if (orientation > 180 - SENSOR_ANGLE && orientation < 180 + SENSOR_ANGLE) {
? ? ? ? ? ? ? ? orientation = 180;
? ? ? ? ? ? } else if (orientation > 270 - SENSOR_ANGLE && orientation < 270 + SENSOR_ANGLE) {
? ? ? ? ? ? ? ? orientation = 270;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? }
? ? }
MyOrientationEventListener listener = new MyOrientationEventListener(this);
? ? ? ? listener.enable();
? ? ? ? listener.disable();

但是,它只有當手機豎直握持,然后左右轉動時是有效的,手機平放,左右轉動,是感應不到角度變化的。原因是OrientationEventListener原理是只采集了Sensor X和Y方向上的加速度進行計算的。可以從下面源碼中看到orientation的值只跟X和Y有關。(下面的源碼取自android.view.OrientationEventListener)而且使用這個判斷還有一個弊端,就是當屏幕實際已經進行旋轉切換,但是OrientationEventListener回調的值還沒到達旋轉后的值。這就導致了系統屏幕旋轉了,但是我們app的UI因為沒有收到callback而沒有改變的問題。

class SensorEventListenerImpl implements SensorEventListener {
? ? ? ? private static final int _DATA_X = 0;
? ? ? ? private static final int _DATA_Y = 1;
? ? ? ? private static final int _DATA_Z = 2;
? ? ? ??
? ? ? ? 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 - (int)Math.round(angle);
? ? ? ? ? ? ? ? // normalize to 0 - 359 range
? ? ? ? ? ? ? ? while (orientation >= 360) {
? ? ? ? ? ? ? ? ? ? orientation -= 360;
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? ? ? while (orientation < 0) {
? ? ? ? ? ? ? ? ? ? orientation += 360;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if (mOldListener != null) {
? ? ? ? ? ? ? ? mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);
? ? ? ? ? ? }
? ? ? ? ? ? if (orientation != mOrientation) {
? ? ? ? ? ? ? ? mOrientation = orientation;
? ? ? ? ? ? ? ? onOrientationChanged(orientation);
? ? ? ? ? ? }
? ? ? ? }

#3 為了解決上述問題,其實最好的就是在系統屏幕旋轉的時候,能有個回調,告訴我當前是哪個角度,這樣就是最準確的了。但是onConfigurationChanged只能告訴你是橫的還是豎的,雖然它做不了,但是給了一個方向。就是屏幕旋轉系統調用onConfigurationChanged的時候,肯定是知道旋轉后的角度的。根據閱讀源碼可知,當屏幕旋轉時,會調用IRotationWatcher#onRotationChanged(),但是對app來說是Hide的api,無法對他進行監聽。然后又發現android.hardware.LegacySensorManager類它在構造函數里面,對IRotationWatcher進行了注冊,onRotationChanged()返回的值,也會保存在sRotation,所以可以在這里做文章了。

public class ScreenOrientationListener extends OrientationEventListener {
?
? ? private static final String TAG = ScreenOrientationListener.class.getSimpleName();
? ? private int mOrientation;
? ? private OnOrientationChangedListener mOnOrientationChangedListener;
? ? private Context mContext;
? ? private Field mFieldRotation;
? ? private Object mOLegacy;
?
? ? public ScreenOrientationListener(Context context) {
? ? ? ? super(context);
? ? ? ? mContext = context;
? ? }
?
? ? public void setOnOrientationChangedListener(OnOrientationChangedListener listener) {
? ? ? ? this.mOnOrientationChangedListener = listener;
? ? }
?
? ? public int getOrientation() {
? ? ? ? int rotation = -1;
? ? ? ? try {
? ? ? ? ? ? if (null == mFieldRotation) {
? ? ? ? ? ? ? ? SensorManager sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
? ? ? ? ? ? ? ? Class clazzLegacy = Class.forName("android.hardware.LegacySensorManager");
? ? ? ? ? ? ? ? Constructor constructor = clazzLegacy.getConstructor(SensorManager.class);
? ? ? ? ? ? ? ? constructor.setAccessible(true);
? ? ? ? ? ? ? ? mOLegacy = constructor.newInstance(sensorManager);
? ? ? ? ? ? ? ? mFieldRotation = clazzLegacy.getDeclaredField("sRotation");
? ? ? ? ? ? ? ? mFieldRotation.setAccessible(true);
? ? ? ? ? ? }
? ? ? ? ? ? rotation = mFieldRotation.getInt(mOLegacy);
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? Log.e(TAG, "getRotation e=" + e.getMessage());
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
// ? ? ? ?Log.d(TAG, "getRotation rotation=" + rotation);
?
? ? ? ? int orientation = -1;
? ? ? ? switch (rotation) {
? ? ? ? ? ? case Surface.ROTATION_0:
? ? ? ? ? ? ? ? orientation = 0;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case Surface.ROTATION_90:
? ? ? ? ? ? ? ? orientation = 90;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case Surface.ROTATION_180:
? ? ? ? ? ? ? ? orientation = 180;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case Surface.ROTATION_270:
? ? ? ? ? ? ? ? orientation = 270;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? break;
? ? ? ? }
// ? ? ? ?Log.d(TAG, "getRotation orientation=" + orientation);
? ? ? ? return orientation;
? ? }
?
? ? @Override
? ? public void onOrientationChanged(int orientation) {
? ? ? ? if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
? ? ? ? ? ? return; // 手機平放時,檢測不到有效的角度
? ? ? ? }
? ? ? ? orientation = getOrientation();
? ? ? ? if (mOrientation != orientation) {
? ? ? ? ? ? mOrientation = orientation;
? ? ? ? ? ? if (null != mOnOrientationChangedListener) {
? ? ? ? ? ? ? ? mOnOrientationChangedListener.onOrientationChanged(mOrientation);
? ? ? ? ? ? ? ? Log.d(TAG, "ScreenOrientationListener onOrientationChanged orientation=" + mOrientation);
? ? ? ? ? ? }
? ? ? ? }
? ? }
?
? ? public interface OnOrientationChangedListener {
? ? ? ? void onOrientationChanged(int orientation);
? ? }
}

上面的代碼,就是通過監聽OrientationEventListener實時角度變化,然后使用反射的方法去獲取LegacySensorManager里面的rotation,這樣拿到的角度就是準確的,在配合角度變化時才回調callback,就完美實現了4個方向角度旋轉時的監聽。

原文鏈接:https://blog.csdn.net/u011803341/article/details/96431142

欄目分類
最近更新