Android实现屏幕旋转四个方向准确监听

在做相机开发时,遇到一个问题,就是需要监听屏幕旋转。最简单的就是使用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个方向角度旋转时的监听。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android6.0开发中屏幕旋转原理与流程分析

    本文实例讲述了Android6.0开发中屏幕旋转原理与流程.分享给大家供大家参考,具体如下: 从Android 系统开发开始,这里写下Android 6.0 屏幕旋转系统分析. 第一部分 Kenel Android 系统屏幕旋转得以实现,是靠从底层驱动gsensor 中获取数据,从而判断屏幕方向的.kernel sensor的驱动先就不在这里赘述,简单介绍下,gsensor 驱动注册input 事件 在/dev/input/下,可以通过adb getevent -p 可以查看系统所有的输入事件.

  • Android webview旋转屏幕导致页面重新加载问题解决办法

    Android webview旋转屏幕导致页面重新加载问题解决办法 1. 在create时候加个状态判断 protected void onCreate(Bundle savedInstanceState){ ... if (savedInstanceState == null) { mWebView.loadUrl("your_url"); } ... } 2. 重载保存状态的函数: @Override protected void onSaveInstanceState(Bundl

  • Android App获取屏幕旋转角度的方法

    本文实例为大家分享了Android App获取屏幕旋转角度的具体代码,供大家参考,具体内容如下 一.获取屏幕旋转角度的方法是:int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); 二.测试代码 1.getRotation\app\src\main\java\com\example\getrotation\MainActivity.java package com.example.getrota

  • Android屏幕旋转之横屏竖屏切换的实现

    刚实现了App内手机横/竖放置时,屏幕横/竖屏的切换.记录一下中间需要的关键信息和实现过程. 开门见山的说,实现屏幕自动/手动旋转的方式有两种: 一种是在工程的代码中定义,这种方式在横竖屏切换时执行的操作是:销毁当前Activity–根据新的屏幕尺寸重建Activity.如果不进行数据存储的操作,在切换的过程中Activity中的数据会丢失. 另一种是在工程的AndroidManifest.xml中定义,这种定义的方式在某些情况下可以实现"不销毁需要横竖屏的Activity",因为这种

  • Android如何监听屏幕旋转

    背景 关于个人,前段时间由于业务太忙,所以一直没有来得及思考并且沉淀点东西:同时组内一个个都在业务上能有自己的思考和总结,在这样的氛围下,不由自主的驱使周末开始写点东西,希望自己除了日常忙于业务,可以沉淀点东西,加上自己的成长.. 关于切入点,最近在做应⽤内悬浮球功能时,需要监听屏幕旋转事件来对悬浮球的位置进⾏调整,发现有些情况下并不能收到系统回调,思考了⼀翻,做了⼀个屏幕旋转的模拟监听,基本上能达到⽬的. 问题 悬浮球在停⽌拖拽后,需要贴边到⼿机屏幕的左右两侧. 在竖屏状态下,x坐标为0即为左

  • Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

    一.概述 运行时变更就是设备在运行时发生变化(例如屏幕旋转.键盘可用性及语言).发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务,以便恢复activity的状态. 为此,google提供了三种解决方案: 对于少量数据: 通过onSaveInstanceState(),保存有关应用状态的数据. 然后在 onCreate() 或 onRestoreInstanceState()期间恢复 Activity 状态. 对于大量数据:用

  • 详解Android中Runtime解决屏幕旋转问题(推荐)

    前言 大家或许在iOS程序开发中经常遇到屏幕旋转问题,比如说希望指定的页面进行不同的屏幕旋转,但由于系统提供的方法是导航控制器的全局方法,无法随意的达到这种需求.一般的解决方案是继承UINavrgationViewController,重写该类的相关方法,这样虽然也能解决问题,但是在重写的过程中至少产生两个多余的文件和不少的代码,这显然不是我们想要的.下面就使用一种较底层的方法解决这个问题. 基本原理 动态的改变UINavrgationViewController的全局方法,将我们自己重写的su

  • Android实现简单旋转动画

    本文实例为大家分享了Android实现简单旋转动画的具体代码,供大家参考,具体内容如下 核心方法 public void startAnimation(Animation animation) 执行动画,参数可以是各种动画的对象,Animation的多态,也可以是组合动画,后面会有. 2个参数的构造方法 /**  * Constructor to use when building a RotateAnimation from code.  * Default pivotX/pivotY poi

  • Android实现屏幕旋转方法总结

    本文实例总结了Android实现屏幕旋转方法.分享给大家供大家参考.具体如下: 在介绍之前,我们需要先了解默认情况下android屏幕旋转的机制: 默认情况下,当用户手机的重力感应器打开后,旋转屏幕方向,会导致当前activity发生onDestroy-> onCreate,这样会重新构造当前activity和界面布局,如果在Camera界面,则表现为卡顿或者黑屏一段时间.如果是在横竖屏UI设计方面,那么想很好地支持屏幕旋转,则建议在res中建立layout-land和layout-port两个

  • Android开发 旋转屏幕导致Activity重建解决方法

    Android开发文档上专门有一小节解释这个问题.简单来说,Activity是负责与用户交互的最主要机制,任何"设置"(Configuration)的改变都可能对Activity的界面造成影响,这时系统会销毁并重建Activity以便反映新的Configuration. "屏幕方向"(orientation)是一个Configuration,通过查看Configuration类的javadoc可以看到其他Configuration还有哪些:如fontScale.ke

随机推荐