和你一起终身学习,这里是程序员Android
本篇文章主要介绍 Android
开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:
一、转屏慢与整体分析思路
二、Google转屏算法
三、 优化方案
一、转屏慢与整体分析思路
我们用手机转屏的时候,会发现有时候转屏特别慢,快的和慢的会相差1倍,例如快的转屏700ms,慢的转屏会1.2s以上.
目前分析思路是:
1、gsenor上报数据是否有延迟?
2、gsensor上报的数据准确后,google的算法需要多长时间才能转换为方向改变?
3、上层处理转屏时间是多长时间?即冻屏时间是多长
4、转屏动画的处理时长?
第一个问题属于sensor部分,我们暂时不讨论。
第二个问题属于Google转屏算法部分,也是我们本次将要重点讨论的内容。
第三方问题属于framework和view layer界面层级部分,这部分属于项目问题,不讨论
第四个问题,这个很好理解,默认android转屏动画都是400ms
,大家可以根据项目要求自定义修改,这个目前市面上的很多大厂家都有做优化,修改也比较简单,此处也不过多阐述。
查看代码发现ScreenRotationAnimation.startAnimation()
方法会调用anim文件
优化横竖屏切换的动画时间,修改代码如下:
比如查看screen_rotate_start_enter.xml
动画文件的源码如下:
frameworks/base/core/res/res/anim/screen_rotate_start_enter.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<scale android:fromXScale="1.0" android:toXScale="0.9"
android:fromYScale="1.0" android:toYScale="0.9"
android:pivotX="50%" android:pivotY="50%"
android:interpolator="@interpolator/decelerate_quint"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>
此时我们需要优化动画的duraion 时间,修改方案如下:
+++ b/idh.code/frameworks/base/core/res/res/values/config.xml
@@ -139,13 +139,13 @@
<bool name="config_disableTransitionAnimation">false</bool>
<!-- The duration (in milliseconds) of a short animation. -->
- <integer name="config_shortAnimTime">200</integer>
+ <integer name="config_shortAnimTime">100</integer>
<!-- The duration (in milliseconds) of a medium-length animation. -->
- <integer name="config_mediumAnimTime">400</integer>
+ <integer name="config_mediumAnimTime">200</integer>
<!-- The duration (in milliseconds) of a long animation. -->
- <integer name="config_longAnimTime">500</integer>
+ <integer name="config_longAnimTime">250</integer>
<!-- The duration (in milliseconds) of the activity open/close and fragment open/close animations. -->
<integer name="config_activityShortDur">150</integer>
二、Google转屏算法
为了能够优化该类问题,大家就不得不去研究一下Google的转屏算法的实现,只有了解其实现原理才能谈的上如何去优化.
代码路径=>
WindowOrientationListener.java (frameworks\base\services\core\java\com\android\server\policy)
这部分算法google很少改动,目前看android 6.0, android 7.0, android 8.0/8.1几乎都没怎么改动过。
内容的话看懂了就还是比较简单的,逻辑都只在一个文件中。
2.1 数据来源
转屏识别方向是通过gsensor重力传感器来实现的,那么数据肯定是从gsensor重力传感器传上来的,这里略过,我们之间到google开始识别部分,看看传递上来给上层识别的数据是什么?
private static final int ACCELEROMETER_DATA_X = 0;
private static final int ACCELEROMETER_DATA_Y = 1;
private static final int ACCELEROMETER_DATA_Z = 2;
//sensor数据改变会传递SensorEvent就是传感器的事件
public void onSensorChanged(SensorEvent event) {
//...
//此处获取x,y,z三个轴的加速度的数据
float x = event.values[ACCELEROMETER_DATA_X];
float y = event.values[ACCELEROMETER_DATA_Y];
float z = event.values[ACCELEROMETER_DATA_Z];
//此处获取事件上报时间的数据
final long now = event.timestamp;
目前Android使用的gsensor数据有3个就是底层传递上来的x、y、z三个坐标轴的加速度方向。
本来gsensor的数据只有一个就是9.8 m/s^2,垂直往下。
但是由于设备(不管是手机还是gsensor)是空间上有3个方向,在一个立体的空间描述一个分类,可以拆分成x、y、z轴的矢量叠加。
至于上报时间这个事件数据是android自身设置,不属于gsensor提供的数据
2.2 识别x,y,z轴
根据sensor传递上来的event事件,由于sensor内部刷新速度可以很快200Hz(5ms一次),需要先进行初步判断和转换
//当前事件的时间
final long now = event.timestamp;
//上一次事件的时间
final long then = mLastFilteredTimestampNanos;
//两次事件间隔,单位是ms,一般默认SENSOR_DELAY_NORMAL时,gsensor上报数据是66.666ms左右
final float timeDeltaMS = (now - then) * 0.000001f;
//是否需要跳过
final boolean skipSample;
//判断是否无效事件,无效重新reset
if (now < then
|| now > then + MAX_FILTER_DELTA_TIME_NANOS
|| (x == 0 && y == 0 && z == 0)) {
//清除之前的数据,重新开始
resetLocked();
skipSample = true;
} else {
//FILTER_TIME_CONSTANT_MS=200ms,timeDeltaMS目前应该是66.666ms左右,alpha= 0.25左右
//x,y,z渐变正常都是以0.25的速率改变
final float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
//...
skipSample = false;
}
x,y,z渐变正常都是以0.25的速率改变。
取部分正常转屏的日志,如下面数据
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
mLastFilteredX是上一个转换后的x=2.6531296,右边的运算x是未转换的x=9.012,左边的x是转换后的x=4.230438,带入上述公式
4.230438 = alpha*(9.012 - 2.6531296) + 2.6531296
alpha = 0.248
//上一次转后的数据
14:20:45.056 Filtered acceleration vector: x=2.6531296, y=9.352881, z=2.019604, magnitude=9.9
//gsensor当前上报的数据
14:20:45.122 Raw acceleration vector: x=9.012, y=-13.896, z=-1.436, magnitude=16.6
//当前gsensor的数据转换后的最新x、y、z轴数据
14:20:45.122 Filtered acceleration vector: x=4.230438, y=3.5860295, z=1.1624464, magnitude=5.6
x,y,z 3轴禁止不动时的值范围是(0-9.8),代表加速度方向,加速度方向是9.8,指向的是垂直往下。
x轴是左边正方向,y轴是竖直向上正方向,z轴是屏幕向上是正方向,
如下图所示
y轴 z轴
-(0,9.8,0) -(0,0,9.8)
- -
- -
- -
- -
x轴(9.8,0,0)--------(x,y,z)(0,0,0)
最终如果没有额外加速度影响,合成的值是大概9.8 m/s^2
2.3 判断x,y,z轴是否有效
x、y、z三轴的矢量怎么转换成一个方向的矢量呢?
在数学算法中求该合成矢量的方法很简单:Math.sqrt(x * x + y * y + z * z),求(x * x + y * y + z * z)方差。(如果是直角三角形,最长边的长度L*L = x * x + y * y => L = Math.sqrt(x * x + y * y)这个大家很好理解,加多一个立体面的z轴其实是一个意思)。
//求x,y,z轴的方差,也就是距离(0,0,0)点的距离,这就的距离就是加速度,0点代表地面
final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
//NEAR_ZERO_MAGNITUDE=1,加速度小于1的可以忽略
//在什么情况下会出现这种事情呢,你拿着手机往地上摔的时候这个就是0啦,小心手机别摔坏了~~
if (magnitude < NEAR_ZERO_MAGNITUDE) {
if (LOG) {
Slog.v(TAG, "Ignoring sensor data, magnitude too close to zero.");
}
clearPredictedRotationLocked();
} else {
//在小于5.8(9.8-4)或者大于13.8(9.8+4),代表手机在加速(往一个方向加速)
//垂直向下加速是小于9.8,垂直向上加速是大于9.8
//非常缓慢转屏的话是9.8左右,你没有加速度
if (isAcceleratingLocked(magnitude)) {
isAccelerating = true;
mAccelerationTimestampNanos = now;
}
- 当加速度小于1的时候,相当于自由落体,google算法部分会忽略。
- 此处既然获取了加速度,顺便对加速的逻辑进行的判断,若总的加速度小于5.8(9.8-4)或者大于13.8(9.8+4),代表手机整体的加速度是有较大的变化,简而言之,说明手机有加速了
2.4 计算z轴倾斜角度
magnitude是距离(0,0,0)的长度(代表加速度方向与大小),z代表z轴加速度,Math.asin(z / magnitude)得出的就是偏向z轴的弧度(根据直角三角形的sin、cos、tan、cot是可以知道该夹角的角度的,如果不了解该逻辑的同学可以百度一下,或者翻一翻中学时度的数学书…),转换成角度就是tiltAngle。
//RADIANS_TO_DEGREES根据弧度换算成角度的的比例,代表每弧度等于多少角度
private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
//magnitude是距离(0,0,0)的长度(代表加速度方向与大小),z代表z轴加速度
final int tiltAngle = (int) Math.round(Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
asin(x)是求反正玄函数,得到的值是弧度,取值x范围是-1<=x<=1
如:
pi=3.14159265359…弧度,或者说是180度
a = sin(pi/6) = sin(30度) = sin(0.5236弧度) = 1/2 = 0.5
b = asin(a) = pi/6 = 0.5236弧度
sin(30度) = 0.5这个大家应该很熟悉吧,不熟悉还是那就话,先翻一下中学数学课本…
2.5 判断是否平躺着,是否有垂直摇摆晃动
- 判断是否有平躺,条件:z轴角度tiltAngle大于80度,而且时间大于1s
- 是否有垂直摇摆晃动,条件:取300ms内的数据,z轴角度变化相差超过20度代表有晃动
//是否有平躺,z轴角度tiltAngle大于80度,而且时间大于1s
if (isFlatLocked(now)) {
//设置平躺标签为true
isFlat = true;
//设置平躺触发时间
mFlatTimestampNanos = now;
}
//是否有垂直摇摆晃动,取300ms内的数据,z轴角度变化相差超过20度代表有晃动
if (isSwingingLocked(now, tiltAngle)) {
//设置摇摆晃动标签为true
isSwinging = true;
//设置摇摆晃动触发时间
mSwingTimestampNanos = now;
}
2.6 垂直倾斜角度过滤
- 当垂直倾斜角度在一定范围内会给过滤掉,即该值不会用于方向识别,进入忽略条件tiltAngle<=-40(代表手机盖住了),退出忽略条件是tiltAngle>=-15
- z倾斜轴角度大于80度的时候,不管是盖住还是朝上放着,都会忽略掉
private static final int TILT_OVERHEAD_ENTER = -40;
private static final int TILT_OVERHEAD_EXIT = -15;
private static final int MAX_TILT = 80;
//进入忽略条件tiltAngle<=-40(代表手机盖住了
if (tiltAngle <= TILT_OVERHEAD_ENTER) {
mOverhead = true;
//退出忽略条件是tiltAngle>=-15
} else if (tiltAngle >= TILT_OVERHEAD_EXIT) {
mOverhead = false;
}
//忽略
if (mOverhead) {
clearPredictedRotationLocked();
//z倾斜轴角度大于80度的时候,不管是盖住还是朝上放着,都会忽略掉
} else if (Math.abs(tiltAngle) > MAX_TILT) {
clearPredictedRotationLocked();
}
2.7 识别转屏角度
- 识别转屏角度orientationAngle,通过反正切得到角度
- 将转屏角度标准化到0-360度
//通过反正切得到角度
int orientationAngle = (int) Math.round(
-Math.atan2(-x, y) * RADIANS_TO_DEGREES);
//角度转换成0-360度
if (orientationAngle < 0) {
// atan2 返回的是 [-180, 180]; 标准化到 [0, 360]
orientationAngle += 360;
}
2.8 构建最接近的方向
构建最接近的方向nearestRotation,
orientationAngle取值是0-360,nearestRotation取值是0-3.
逆时针从竖屏方向旋转分别是:
0(垂直正常手握方向) - 1(左侧横屏) - 2(垂直反方向,手机倒着拿的方向) - 3(右侧横屏)
每个方向对应的真实角度是:
0 => 0
1 => 90
2 => 180
3 => 270
//如该角度小于某个真实物理方向45度以内的,都算在该物理方向内
int nearestRotation = (orientationAngle + 45) / 90;
if (nearestRotation == 4) {
nearestRotation = 0;
}
构建后各个角度的最接近方向如下:
0-45&&315-360 => 0
45-135 => 1
135-225 => 2
225-315 => 3
2.9 判断数据是否有效
这里主要是2个逻辑
- 判断tilt倾斜角度,tiltAngle正值代表屏幕向上,负值代表屏幕向下.如方向0时(正常竖屏)倾斜角度不能超过:屏幕向上不能超过70度,屏幕盖住不能超过-25度
- 判断旋转方向是否可以接受,此处旋转角度必须接近实际角度(0,90,180,270)的25度以内才算方向改变
//判断tilt倾斜角度,tiltAngle正值代表屏幕向上,负值代表屏幕向下
private boolean isTiltAngleAcceptableLocked(int rotation, int tiltAngle) {
return tiltAngle >= mTiltToleranceConfig[rotation][0]
&& tiltAngle <= mTiltToleranceConfig[rotation][1];
}
//上述举个例子,如方向0时(正常竖屏)倾斜角度不能超过:屏幕向上不能超过70度,屏幕盖住不能超过-25度
private final int[][] mTiltToleranceConfig = new int[][] {
/* ROTATION_0 */ { -25, 70 },
/* ROTATION_90 */ { -25, 65 },
/* ROTATION_180 */ { -25, 60 },
/* ROTATION_270 */ { -25, 65 }
};
//isOrientationAngleAcceptableLocked判断方向是否可以接受,
//旋转角度必须接近实际角度(0,90,180,270)的25度以内才算方向改变
//rotation是本次最接近的方向 nearestRotation(0,1,2,3)(0,90,180,270),
//orientationAngle是本身实际的方向(0-360),nearestRotation对应的角度如下:
//0-45&&315-360 => 0
//45-135 => 1
//135-225 => 2
//225-315 => 3
private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
private boolean isOrientationAngleAcceptableLocked(int rotation, int orientationAngle) {
//mCurrentRotation是上一次的方向,rotation是当前最接近的方向
final int currentRotation = mCurrentRotation;
if (currentRotation >= 0) {
//方向不变化或者,方向往逆时针加90旋转
if (rotation == currentRotation
|| rotation == (currentRotation + 1) % 4) {
//也就是0-360减去22.5度,(0,1,2,3)(-22.5,67.5,157.5,247.5)
int lowerBound = rotation * 90 - 45
+ ADJACENT_ORIENTATION_ANGLE_GAP / 2;
//如果当前最接近的方向是竖直方向(0-45&&315-360)
if (rotation == 0) {
//lowerBound=-22.5,也就是315 <= orientationAngle < 337.5范围内会给丢弃
//也就是说从270度(右侧横屏幕)转到0度或360度(竖直方向),会将0的范围缩短为(0-45&&337.5-360)
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
return false;
}
//如果是其它方向
} else {
//这个是上面得到的,也就是与实际方向相差25度以内的才会给使用
//0-45&&337.5-360 => 0
//下面的是本次得到
//67.5-135 => 1
//157.5-225 => 2
//247.5-315 => 3
//相差25度以上的丢弃
if (orientationAngle < lowerBound) {
return false;
}
}
}
//顺时针旋转,与上面逻辑类似
if (rotation == currentRotation
|| rotation == (currentRotation + 3) % 4) {
//也就是0-360加上22.5度,(0,1,2,3)(22.5,112.5,202.5,292.5)
int upperBound = rotation * 90 + 45
- ADJACENT_ORIENTATION_ANGLE_GAP / 2;
//如果当前最接近的方向是竖直方向(0-45&&315-360)
if (rotation == 0) {
//也就是说从90度(左侧横屏幕)转到0度或360度(竖直方向),会将0的范围缩短为(22.5-45&&315-360)
if (orientationAngle <= 45 && orientationAngle > upperBound) {
return false;
}
} else {
//这个是上面得到的,也就是与实际方向相差25度以内的才会给使用
//22.5-45&&315-360 => 0
//下面的是本次得到
//45-112.5 => 1
//135-202.5 => 2
//225-292.5 => 3
//相差25度以上的丢弃
if (orientationAngle > upperBound) {
return false;
}
}
}
}
return true;
}
2.10 判断是否需要更新方向信息
上面已经获得了方向信息,那是否需要更新还是有逻辑进行控制:
- 当前角度需要保持40ms以上
- 从手机平放着拿起需要500ms才会转屏
- 晃动后300ms内都不能转屏
- 加速转动的时候500ms都不能转屏
- 手没有触摸屏幕500ms才能转屏
//判断当前的方向改变是否需要更新到系统
private boolean isPredictedRotationAcceptableLocked(long now) {
//mPredictedRotationTimestampNanos是上一次更新有效数据的时间(方向改变才会设置),
//在上面的updatePredictedRotationLocked设置过
//当前角度需要保持40ms以上
if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
return false;
}
//从手机平放着拿起需要500ms才会转屏
if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
return false;
}
//晃动后300ms内都不能转屏
if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
return false;
}
//加速转动的时候500ms都不能转屏
if (now < mAccelerationTimestampNanos
+ PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
return false;
}
//手没有触摸屏幕500ms才能转屏
if (mTouched || now < mTouchEndedTimestampNanos
+ PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
return false;
}
// Looks good!
return true;
}
从上述我们可以看到,就算识别了方向,也还需要一段时间才让系统去更新。
三、 优化方案
从google算法,我们可以知道其有2个比较大的缺陷:
- 通过重力传感器传来的x,y,z轴的加速度合成之后只有一个垂直往下的加速度,如果此时用户在别的方向上有加速度,那么通过反余弦、反正切等计算的角度就会不准确,原始的角度计算有问题,就谈不上识别正确的方向了。而google在旋转角度上,并未用代码进行修正。
- google拿到方向信息之后,去干扰采取的措施是延迟300-500ms,等彻底没有干扰的时候再进行方向改变,这个方案很保守。由于普通用户正常转屏都会触发抖动和加速(70%左右都会触发),这样就会导致,就算是旋转角度计算没有任何问题,也会由于算法导致300-500ms的延迟。
上述2个缺陷有兴趣的同学可以自己去修正它。
在这里提供一个简单且容易实现的方案:
对第2)点google延迟的缺陷进行修正=>
- 保留之前N个旋转角度数据(N可以自己根据实际调整)。
- 增加开始转屏逻辑(给个提示:旋转的时候角度会往一个方向增加/减少,那么单位时间内增加/减少多少角度可以作为一个评判标准;开始和结束的位置逻辑可以做为另一个评判标准)。
- 增加转屏完成的逻辑(给个提示:转屏完成之后角度变化会比较缓慢或者不动,其会在实际角度周围)。
增加上述逻辑对Google的延迟进行修正,这种做法属于保守修正,可以优化转屏速度大概200-400ms左右。这里不提供实际代码,仅供思路参考。
+++ b/[idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca;hb=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca)
@@ [-294,7](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=d5adb5e1c111c9860d0f1c739cd83e0126715b92;hb=d5adb5e1c111c9860d0f1c739cd83e0126715b92#l294) [+294,7](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca;hb=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca#l294) @@ public abstract class WindowOrientationListener {
// The minimum amount of time that must have elapsed since the screen was last touched
// before the proposed rotation can change.
protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
- 500 * NANOS_PER_MS;
+ 300 * NANOS_PER_MS;
/**
* Gets the proposed rotation.
@@ [-405,17](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=d5adb5e1c111c9860d0f1c739cd83e0126715b92;hb=d5adb5e1c111c9860d0f1c739cd83e0126715b92#l405) [+405,17](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca;hb=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca#l405) @@ public abstract class WindowOrientationListener {
// The minimum amount of time that must have elapsed since the device last exited
// the flat state (time since it was picked up) before the proposed rotation
// can change.
- private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 500 * NANOS_PER_MS;
+ private static final long PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS = 300 * NANOS_PER_MS;
// The minimum amount of time that must have elapsed since the device stopped
// swinging (time since device appeared to be in the process of being put down
// or put away into a pocket) before the proposed rotation can change.
- private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 300 * NANOS_PER_MS;
+ private static final long PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS = 150 * NANOS_PER_MS;
// The minimum amount of time that must have elapsed since the device stopped
// undergoing external acceleration before the proposed rotation can change.
private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
- 500 * NANOS_PER_MS;
+ 300 * NANOS_PER_MS;
// If the tilt angle remains greater than the specified angle for a minimum of
// the specified time, then the device is deemed to be lying flat
@@ [-502,7](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=d5adb5e1c111c9860d0f1c739cd83e0126715b92;hb=d5adb5e1c111c9860d0f1c739cd83e0126715b92#l502) [+502,7](http://192.168.11.104/gitweb/?p=MOCORDROIDQ_Trunk_W19.xx.x.git;a=blob;f=idh.code/frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java;h=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca;hb=1c83e08806e1b630eb2b2b31c4e33f90d7eec6ca#l502) @@ public abstract class WindowOrientationListener {
// adjacent orientation. No orientation proposal is made when the orientation
// angle is within the gap between the current orientation and the adjacent
// orientation.
- private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 45;
+ private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 30;
// The tilt angle range in degrees for each orientation.
// Beyond these tilt angles, we don't even consider transitioning into the
ps: 原文内容有所更改
原文链接:csdn/yun_hen/java/article/details/78799172
至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!
网友评论