Android實現(xiàn)屏幕旋轉四個方向準確監(jiān)聽
在做相機開發(fā)時,遇到一個問題,就是需要監(jiān)聽屏幕旋轉。最簡單的就是使用onConfigurationChanged()和OrientationEventListener這兩種方法來實現(xiàn),但是最后都遇到了問題。
#1 一開始是使用onConfigurationChanged()這個回調,重新Activity里面的這個方法就可以了,簡單又方便。用了之后發(fā)現(xiàn),它只能監(jiān)聽,橫屏切豎屏的情況。左橫屏切右橫屏是監(jiān)聽不到的,而且切完之后你也不知道是左橫屏還是右橫屏。下面是使用onConfigurationChanged()進行監(jiān)聽的簡單使用。
@Override
? ? public void onConfigurationChanged(Configuration newConfig) {
? ? ? ? super.onConfigurationChanged(newConfig);
? ? ? ? if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
? ? ? ? ? ? // 橫屏
? ? ? ? }else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
? ? ? ? ? ? // 豎屏
? ? ? ? }
? ? }#2 之后又想到了OrientationEventListener來監(jiān)聽屏幕旋轉的實時角度,這個非常靈活,手機轉動實時角度都會回調出來。下面是使用OrientationEventListener的簡單實現(xiàn)。在適當?shù)奈恢谜{用enable()和disable()來開啟和關閉監(jiān)聽。
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方向上的加速度進行計算的??梢詮南旅嬖创a中看到orientation的值只跟X和Y有關。(下面的源碼取自android.view.OrientationEventListener)而且使用這個判斷還有一個弊端,就是當屏幕實際已經(jīng)進行旋轉切換,但是OrientationEventListener回調的值還沒到達旋轉后的值。這就導致了系統(tǒng)屏幕旋轉了,但是我們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 為了解決上述問題,其實最好的就是在系統(tǒng)屏幕旋轉的時候,能有個回調,告訴我當前是哪個角度,這樣就是最準確的了。但是onConfigurationChanged只能告訴你是橫的還是豎的,雖然它做不了,但是給了一個方向。就是屏幕旋轉系統(tǒng)調用onConfigurationChanged的時候,肯定是知道旋轉后的角度的。根據(jù)閱讀源碼可知,當屏幕旋轉時,會調用IRotationWatcher#onRotationChanged(),但是對app來說是Hide的api,無法對他進行監(jiān)聽。然后又發(fā)現(xiàn)android.hardware.LegacySensorManager類它在構造函數(shù)里面,對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);
? ? }
}上面的代碼,就是通過監(jiān)聽OrientationEventListener實時角度變化,然后使用反射的方法去獲取LegacySensorManager里面的rotation,這樣拿到的角度就是準確的,在配合角度變化時才回調callback,就完美實現(xiàn)了4個方向角度旋轉時的監(jiān)聽。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Android應用中使用ViewPager和ViewPager指示器來制作Tab標簽
這篇文章主要介紹了Android中使用ViewPager和ViewPager指示器來制作Tab標簽的方法,ViewPager指示器ViewPageIndicator是一個開源庫,文中舉了一個仿網(wǎng)易新聞客戶端Tab標簽的例子,需要的朋友可以參考下2016-03-03
Android連接MySQL數(shù)據(jù)庫并進行增刪改查操作示例講解
這篇文章主要介紹了Android 連接MySQL數(shù)據(jù)庫并進行增刪改查操作示例講解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08
Android使用ViewFlipper實現(xiàn)圖片切換功能
這篇文章主要為大家詳細介紹了Android使用ViewFlipper實現(xiàn)圖片切換功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
android使用Ultra-PullToRefresh實現(xiàn)下拉刷新自定義代碼
本篇文章主要介紹了android使用Ultra-PullToRefresh實現(xiàn)下拉刷新新自定義,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02
android activity設置無標題實現(xiàn)全屏
本文將詳細介紹Android如何設置Activity全屏和無標題的實現(xiàn)方法,需要的朋友可以參考下2012-12-12

