文章目录

  • 如何判断手机的横竖屏
  • 使用加速度计实现
    • 接口使用
  • 使用加速度计和磁力计获取手机倾斜角度
  • 加速度传感器在Android横竖屏切换中的应用

如何判断手机的横竖屏

这个问题依赖与手机上的传感器
一般获取手机倾斜角度,用加速度计和磁力计去判断
如果只是判断横竖屏 可以只用加速度计
加速度计普及度高

使用加速度计实现

参考了AOSP的通用实现

public class AccelerometerListener {// Device orientationpublic static final int ORIENTATION_UNKNOWN = 0;public static final int ORIENTATION_VERTICAL = 1;public static final int ORIENTATION_HORIZONTAL = 2;private static final String TAG = "AccelerometerListener";private static final boolean DEBUG = true;private static final boolean VDEBUG = false;private static final int ORIENTATION_CHANGED = 1234;private static final int VERTICAL_DEBOUNCE = 100;private static final int HORIZONTAL_DEBOUNCE = 500;private static final double VERTICAL_ANGLE = 50.0;private SensorManager sensorManager;private Sensor sensor;// mOrientation is the orientation value most recently reported to the client.private int orientation;// mPendingOrientation is the latest orientation computed based on the sensor value.// This is sent to the client after a rebounce delay, at which point it is copied to// mOrientation.private int pendingOrientation;private OrientationListener listener;Handler handler =new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case ORIENTATION_CHANGED:synchronized (this) {orientation = pendingOrientation;if (DEBUG) {LogUtil.d(TAG,"orientation: "+ (orientation == ORIENTATION_HORIZONTAL? "horizontal": (orientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));}if (listener != null) {listener.orientationChanged(orientation);}}break;}}};SensorEventListener sensorListener =new SensorEventListener() {@Overridepublic void onSensorChanged(SensorEvent event) {onSensorEvent(event.values[0], event.values[1], event.values[2]);}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// ignore}};public AccelerometerListener(Context context) {sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);}public void setListener(OrientationListener listener) {this.listener = listener;}public void enable(boolean enable) {if (DEBUG) {LogUtil.d(TAG, "enable(" + enable + ")");}synchronized (this) {if (enable) {orientation = ORIENTATION_UNKNOWN;pendingOrientation = ORIENTATION_UNKNOWN;sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);} else {sensorManager.unregisterListener(sensorListener);handler.removeMessages(ORIENTATION_CHANGED);}}}private void setOrientation(int orientation) {synchronized (this) {if (pendingOrientation == orientation) {// Pending orientation has not changed, so do nothing.return;}// Cancel any pending messages.// We will either start a new timer or cancel alltogether// if the orientation has not changed.handler.removeMessages(ORIENTATION_CHANGED);if (this.orientation != orientation) {// Set timer to send an event if the orientation has changed since its// previously reported value.pendingOrientation = orientation;final Message m = handler.obtainMessage(ORIENTATION_CHANGED);// set delay to our debounce timeoutint delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);handler.sendMessageDelayed(m, delay);} else {// no message is pendingpendingOrientation = ORIENTATION_UNKNOWN;}}}private void onSensorEvent(double x, double y, double z) {if (VDEBUG) {LogUtil.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");}// If some values are exactly zero, then likely the sensor is not powered up yet.// ignore these events to avoid false horizontal positives.if (x == 0.0 || y == 0.0 || z == 0.0) {return;}// magnitude of the acceleration vector projected onto XY planefinal double xy = Math.hypot(x, y);// compute the vertical angledouble angle = Math.atan2(xy, z);// convert to degreesangle = angle * 180.0 / Math.PI;final int orientation =(angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);if (VDEBUG) {LogUtil.d(TAG, "angle: " + angle + " orientation: " + orientation);}setOrientation(orientation);}public interface OrientationListener {void orientationChanged(int orientation);}
}

接口使用


public class ProximitySensorimplements AccelerometerListener.OrientationListener, InCallStateListener, AudioModeListener {private static final String TAG = ProximitySensor.class.getSimpleName();private final PowerManager powerManager;private final PowerManager.WakeLock proximityWakeLock;private final AudioModeProvider audioModeProvider;private final AccelerometerListener accelerometerListener;private final ProximityDisplayListener displayListener;private int orientation = AccelerometerListener.ORIENTATION_UNKNOWN;private boolean uiShowing = false;private boolean isPhoneOffhook = false;private boolean dialpadVisible;private boolean isAttemptingVideoCall;private boolean isVideoCall;private boolean isRttCall;public ProximitySensor(@NonNull Context context,@NonNull AudioModeProvider audioModeProvider,@NonNull AccelerometerListener accelerometerListener) {Trace.beginSection("ProximitySensor.Constructor");powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {proximityWakeLock =powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);} else {LogUtil.i("ProximitySensor.constructor", "Device does not support proximity wake lock.");proximityWakeLock = null;}this.accelerometerListener = accelerometerListener;this.accelerometerListener.setListener(this);displayListener =new ProximityDisplayListener((DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE));displayListener.register();this.audioModeProvider = audioModeProvider;this.audioModeProvider.addListener(this);Trace.endSection();}public void tearDown() {audioModeProvider.removeListener(this);accelerometerListener.enable(false);displayListener.unregister();turnOffProximitySensor(true);}/** Called to identify when the device is laid down flat. */@Overridepublic void orientationChanged(int orientation) {this.orientation = orientation;updateProximitySensorMode();}/** Called to keep track of the overall UI state. */@Overridepublic void onStateChange(InCallState oldState, InCallState newState, CallList callList) {// We ignore incoming state because we do not want to enable proximity// sensor during incoming call screen. We check hasLiveCall() because a disconnected call// can also put the in-call screen in the INCALL state.boolean hasOngoingCall = InCallState.INCALL == newState && callList.hasLiveCall();boolean isOffhook =InCallState.PENDING_OUTGOING == newState|| InCallState.OUTGOING == newState|| hasOngoingCall;DialerCall activeCall = callList.getActiveCall();boolean isVideoCall = activeCall != null && activeCall.isVideoCall();boolean isRttCall = activeCall != null && activeCall.isActiveRttCall();if (isOffhook != isPhoneOffhook|| this.isVideoCall != isVideoCall|| this.isRttCall != isRttCall) {isPhoneOffhook = isOffhook;this.isVideoCall = isVideoCall;this.isRttCall = isRttCall;orientation = AccelerometerListener.ORIENTATION_UNKNOWN;accelerometerListener.enable(isPhoneOffhook);updateProximitySensorMode();}}@Overridepublic void onAudioStateChanged(CallAudioState audioState) {updateProximitySensorMode();}public void onDialpadVisible(boolean visible) {dialpadVisible = visible;updateProximitySensorMode();}public void setIsAttemptingVideoCall(boolean isAttemptingVideoCall) {LogUtil.i("ProximitySensor.setIsAttemptingVideoCall","isAttemptingVideoCall: %b",isAttemptingVideoCall);this.isAttemptingVideoCall = isAttemptingVideoCall;updateProximitySensorMode();}/** Used to save when the UI goes in and out of the foreground. */public void onInCallShowing(boolean showing) {if (showing) {uiShowing = true;// We only consider the UI not showing for instances where another app took the foreground.// If we stopped showing because the screen is off, we still consider that showing.} else if (powerManager.isScreenOn()) {uiShowing = false;}updateProximitySensorMode();}void onDisplayStateChanged(boolean isDisplayOn) {LogUtil.i("ProximitySensor.onDisplayStateChanged", "isDisplayOn: %b", isDisplayOn);accelerometerListener.enable(isDisplayOn);}/*** TODO: There is no way to determine if a screen is off due to proximity or if it is legitimately* off, but if ever we can do that in the future, it would be useful here. Until then, this* function will simply return true of the screen is off. TODO: Investigate whether this can be* replaced with the ProximityDisplayListener.*/public boolean isScreenReallyOff() {return !powerManager.isScreenOn();}private void turnOnProximitySensor() {if (proximityWakeLock != null) {if (!proximityWakeLock.isHeld()) {LogUtil.i("ProximitySensor.turnOnProximitySensor", "acquiring wake lock");proximityWakeLock.acquire();} else {LogUtil.i("ProximitySensor.turnOnProximitySensor", "wake lock already acquired");}}}private void turnOffProximitySensor(boolean screenOnImmediately) {if (proximityWakeLock != null) {if (proximityWakeLock.isHeld()) {LogUtil.i("ProximitySensor.turnOffProximitySensor", "releasing wake lock");int flags = (screenOnImmediately ? 0 : PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY);proximityWakeLock.release(flags);} else {LogUtil.i("ProximitySensor.turnOffProximitySensor", "wake lock already released");}}}/*** Updates the wake lock used to control proximity sensor behavior, based on the current state of* the phone.** <p>On devices that have a proximity sensor, to avoid false touches during a call, we hold a* PROXIMITY_SCREEN_OFF_WAKE_LOCK wake lock whenever the phone is off hook. (When held, that wake* lock causes the screen to turn off automatically when the sensor detects an object close to the* screen.)** <p>This method is a no-op for devices that don't have a proximity sensor.** <p>Proximity wake lock will be released if any of the following conditions are true: the audio* is routed through bluetooth, a wired headset, or the speaker; the user requested, received a* request for, or is in a video call; or the phone is horizontal while in a call.*/private synchronized void updateProximitySensorMode() {Trace.beginSection("ProximitySensor.updateProximitySensorMode");final int audioRoute = audioModeProvider.getAudioState().getRoute();boolean screenOnImmediately =(CallAudioState.ROUTE_WIRED_HEADSET == audioRoute|| CallAudioState.ROUTE_SPEAKER == audioRoute|| CallAudioState.ROUTE_BLUETOOTH == audioRoute|| isAttemptingVideoCall|| isVideoCall|| isRttCall);// We do not keep the screen off when the user is outside in-call screen and we are// horizontal, but we do not force it on when we become horizontal until the// proximity sensor goes negative.final boolean horizontal = (orientation == AccelerometerListener.ORIENTATION_HORIZONTAL);screenOnImmediately |= !uiShowing && horizontal;// We do not keep the screen off when dialpad is visible, we are horizontal, and// the in-call screen is being shown.// At that moment we're pretty sure users want to use it, instead of letting the// proximity sensor turn off the screen by their hands.screenOnImmediately |= dialpadVisible && horizontal;LogUtil.i("ProximitySensor.updateProximitySensorMode","screenOnImmediately: %b, dialPadVisible: %b, "+ "offHook: %b, horizontal: %b, uiShowing: %b, audioRoute: %s",screenOnImmediately,dialpadVisible,isPhoneOffhook,orientation == AccelerometerListener.ORIENTATION_HORIZONTAL,uiShowing,CallAudioState.audioRouteToString(audioRoute));if (isPhoneOffhook && !screenOnImmediately) {LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning on proximity sensor");// Phone is in use!  Arrange for the screen to turn off// automatically when the sensor detects a close object.turnOnProximitySensor();} else {LogUtil.v("ProximitySensor.updateProximitySensorMode", "turning off proximity sensor");// Phone is either idle, or ringing.  We don't want any special proximity sensor// behavior in either case.turnOffProximitySensor(screenOnImmediately);}Trace.endSection();}/*** Implementation of a {@link DisplayListener} that maintains a binary state: Screen on vs screen* off. Used by the proximity sensor manager to decide whether or not it needs to listen to* accelerometer events.*/public class ProximityDisplayListener implements DisplayListener {private DisplayManager displayManager;private boolean isDisplayOn = true;ProximityDisplayListener(DisplayManager displayManager) {this.displayManager = displayManager;}void register() {displayManager.registerDisplayListener(this, null);}void unregister() {displayManager.unregisterDisplayListener(this);}@Overridepublic void onDisplayRemoved(int displayId) {}@Overridepublic void onDisplayChanged(int displayId) {if (displayId == Display.DEFAULT_DISPLAY) {final Display display = displayManager.getDisplay(displayId);final boolean isDisplayOn = display.getState() != Display.STATE_OFF;// For call purposes, we assume that as long as the screen is not truly off, it is// considered on, even if it is in an unknown or low power idle state.if (isDisplayOn != this.isDisplayOn) {this.isDisplayOn = isDisplayOn;onDisplayStateChanged(this.isDisplayOn);}}}@Overridepublic void onDisplayAdded(int displayId) {}}
}

使用加速度计和磁力计获取手机倾斜角度

public class MainActivity extends Activity {private SensorManager mSensorManager;private Sensor accelerometer; // 加速度传感器private Sensor magnetic; // 地磁场传感器private TextView azimuthAngle;private float[] accelerometerValues = new float[3];private float[] magneticFieldValues = new float[3];private static final String TAG = "---MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 实例化传感器管理者mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);// 初始化加速度传感器accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);// 初始化地磁场传感器magnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);azimuthAngle = (TextView) findViewById(R.id.azimuth_angle_value);calculateOrientation();}@Overrideprotected void onResume() {// TODO Auto-generated method stub// 注册监听mSensorManager.registerListener(new MySensorEventListener(),accelerometer, Sensor.TYPE_ACCELEROMETER);mSensorManager.registerListener(new MySensorEventListener(), magnetic,Sensor.TYPE_MAGNETIC_FIELD);super.onResume();}@Overrideprotected void onPause() {// TODO Auto-generated method stub// 解除注册mSensorManager.unregisterListener(new MySensorEventListener());super.onPause();}// 计算方向private void calculateOrientation() {float[] values = new float[3];float[] R = new float[9];SensorManager.getRotationMatrix(R, null, accelerometerValues,magneticFieldValues);SensorManager.getOrientation(R, values);values[0] = (float) Math.toDegrees(values[0]);Log.i(TAG, values[0] + "");if (values[0] >= -5 && values[0] < 5) {azimuthAngle.setText("正北");} else if (values[0] >= 5 && values[0] < 85) {// Log.i(TAG, "东北");azimuthAngle.setText("东北");} else if (values[0] >= 85 && values[0] <= 95) {// Log.i(TAG, "正东");azimuthAngle.setText("正东");} else if (values[0] >= 95 && values[0] < 175) {// Log.i(TAG, "东南");azimuthAngle.setText("东南");} else if ((values[0] >= 175 && values[0] <= 180)|| (values[0]) >= -180 && values[0] < -175) {// Log.i(TAG, "正南");azimuthAngle.setText("正南");} else if (values[0] >= -175 && values[0] < -95) {// Log.i(TAG, "西南");azimuthAngle.setText("西南");} else if (values[0] >= -95 && values[0] < -85) {// Log.i(TAG, "正西");azimuthAngle.setText("正西");} else if (values[0] >= -85 && values[0] < -5) {// Log.i(TAG, "西北");azimuthAngle.setText("西北");}}class MySensorEventListener implements SensorEventListener {@Overridepublic void onSensorChanged(SensorEvent event) {// TODO Auto-generated method stubif (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {accelerometerValues = event.values;}if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {magneticFieldValues = event.values;}calculateOrientation();}@Overridepublic void onAccuracyChanged(Sensor sensor, int accuracy) {// TODO Auto-generated method stub}}}

加速度传感器在Android横竖屏切换中的应用

  • 通过监听加速度传感器,实时获得X、Y、Z三个方向的加速度值;当4(XX + YY)>=Z*Z时,开始计算设备在X与Y平面上的旋转角度;最后根据旋转角度计算出设备的横竖屏状态。
/**
* 在onResume中,向SensorManager注册监听加速度传感器
*/
@Override
protected void onResume() {super.onResume();orientationListener = new OrientationListener(this);orientationListener.enable();
}public void enable() {if (mSensor == null) {Log.w(TAG, "Cannot detect sensors. Not enabled");return;}if (mEnabled == false) {if (localLOGV) Log.d(TAG, "OrientationEventListener enabled");mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);mEnabled = true;}
}
``
```java
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;/**** 通过X、Y、Z三个方向的加速度值的变化,计算出设备旋转的角度。**/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 valueif (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 rangewhile (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);}}
}
public class OrientationListener extends OrientationEventListener {private int degree = 0;//旋转角度private int mOrientation = 0;//2—横屏 1-竖屏,0-未知public OrientationListener(Context context) {super(context);}/*** 根据旋转的角度,得出设备横竖屏状态**/@Overridepublic void onOrientationChanged(int orientation) {degree = orientation;if (degree > 0 && degree < 45) {mOrientation = 1;} else if (degree > 45 && degree < 135) {mOrientation = 2;} else if (degree > 135 && degree < 225) {mOrientation = 1;} else if (degree > 225 && degree < 315) {mOrientation = 2;} else if (degree > 315 && degree < 360) {mOrientation = 1;} if (mOrientation == 2) {Log.i("OrientationListener ", "横屏");} else if (mOrientation == 1) {Log.i("OrientationListener ", "竖屏");}}
}

特别说明:Math.atan2(-Y, X) 是计算从原点(0,0)到(x,y)点的线段与x轴正方向之间的平面角度(弧度值),
float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi得到的是原点(0,0)到(x,y)点的线段与x轴正方向之间的角度,90 - (int)Math.round(angle)为设备旋转的角度。

手机加速度传感器在Android横竖屏切换中的应用相关推荐

  1. Android 横竖屏切换

    2019独角兽企业重金招聘Python工程师标准>>> Android开发中,大多APP可能根据实际情况直接将APP的界面方向设死了,或竖屏或横屏.但是,我们还是会遇到横竖屏切换的功 ...

  2. Android横竖屏切换小结

    (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/franksunny/635350788930000000.pdf) Android手机或平板都会存在横竖 ...

  3. Android横竖屏切换相关知识点

    转载自:http://www.cnblogs.com/franksunny/p/3714442.html (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/ ...

  4. Android横竖屏切换小结(重建、非重建Activity)

    来自:http://www.cnblogs.com/franksunny/p/3714442.html (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/f ...

  5. Android 横竖屏切换小结

    (自己体会:每次横竖屏自动切时都会run Activity的onCreate,即相当后重新进入Activity初始化一样:) 转自:http://www.cnblogs.com/franksunny/ ...

  6. Android横竖屏切换

    尊重原创,本文转载自 http://www.cnblogs.com/franksunny/p/3714442.html Android横竖屏切换小结 (老样子,图片啥的详细文档,可以下载后观看 htt ...

  7. Android横竖屏切换重载问题与小结

    (转自:http://www.cnblogs.com/franksunny/p/3714442.html) (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com ...

  8. android横竖屏切换总结

    Android横竖屏切换总结 (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/franksunny/635350788930000000.pdf) And ...

  9. Android—横竖屏切换小结

    Android横竖屏切换小结 (老样子,图片啥的详细文档,可以下载后观看 http://files.cnblogs.com/franksunny/635350788930000000.pdf) And ...

最新文章

  1. Python multiprocess 多进程模块
  2. 美团酒店Node全栈开发实践
  3. Java面试题 20在面向对象编程里,经常使用is-a来说明对象之间的继承关系
  4. 实话!程序员大都不喜欢拉帮结派
  5. JS数组去重算法实现
  6. (11)FPGA复位设计原则
  7. 【毕业答辩】如何制作优秀的毕业论文答辩PPT?
  8. 接口测试——Jmeter常见问题解决方法
  9. Oracle 进程 说明
  10. Centos7搭建虚拟用户FTP
  11. openssh升级sftp_linux搭建sftp(openssh)
  12. 射频电路设计——射频器件基础
  13. opencv+海康威视网络摄像头实时预览+抓图(环境配置加程序)
  14. html文档基本格式主要包括以下哪几类,CAD多选题考试题「附答案」
  15. 手机构建Linux环境,Linux手机DIY.构建统一安装包
  16. 微分dx、dy是无穷小吗?
  17. pythoncookie自动登录_Python爬虫连载6-cookie深入使用实例化实现自动登录
  18. unity做一个小游戏(适合零基础或者巩固加深unity中的工具类的用法)
  19. 河南科技学院对口计算机分数线,河南科技学院09对口升学分数线
  20. 智能网联汽车——底盘线控系统

热门文章

  1. PATA 1093 有几个PAT
  2. 【信息资源管理】第一章:信息资源管理基础
  3. 计算机等级考试php,计算机等级考试查询系统
  4. Centos 7.9 xfs 文件系统测试
  5. SDRAM,DDR1,DDR2,DDR3,DDR4的区别及其特点
  6. DDD为什么能火起来?DDD 与微服务到底是啥关系!
  7. Spring Boot教程十六:SpringBoot注入类实现多线程
  8. Список систем управления базами данных России в 2022 году
  9. .NET 5.0 正式版发布了!
  10. Jenkins+ansible+Tomcat实现项目远程自动部署