在重识Activity—Activity的生命周期这篇文章中有记录Activity横竖屏切换的生命周期,这篇文章主要延伸上篇的该知识点,记录关于Activity横竖屏的那些事儿。
影响横竖屏切换的因素
一般我们在玩游戏或者看视频的时候,会发现手机的界面会由竖屏切换成横屏,等到退出的时候又会由横屏切换成竖屏,那么影响横竖屏切换的因素有哪些呢?
- 在手机的系统设置中,可以设置方向是否锁定,锁定时是竖屏,没有锁定时可以旋转切换;
- 根据手机的物理传感器感应到设备的方向;
- 在App中的代码设置横竖屏属性。
注意:以上三个因素没有所谓的优先级,所以,我们即使在系统设置中锁定了方向,但是如果在App的代码中设置了方向,也会根据代码中设置的属性设置屏幕的方向,不过仅限于App中设置的界面。
横竖屏的设置
因为是通过属性设置的,首先先看一下设置横竖屏有哪些属性值呢?
属性值 | 说明 |
---|---|
unspecified | 默认值。由系统选择方向。 |
behind | 与Activity栈中紧接着它的Activity的方向相同。 |
landscape | 横向方向(显示的宽度大于高度) |
portrait | 纵向方向(显示的高度大于宽度) |
reverseLandscape | 与正常横向方向相反的横向方向。API 9及以上。 |
reversePortrait | 与正常纵向方向相反的纵向方向。API 9及以上。 |
sensorLandscape | 横向方向,但根据设备传感器,可以是正常或者反向的横向方向。API 9及以上。 |
sensorPortrait | 纵向方向,但根据设备传感器,可以是正常或者反向的纵向方向。API 9及以上。 |
userLandscape | 横向方向,但根据设备传感器和用户的传感器首选项,可以是正常或反向的横向方向。 如果用户锁定了基于传感器的旋转,其行为与 landscape 相同,否则,其行为与 sensorLandscape 相同。API 18及以上。 |
userPortrait | 纵向方向,但根据设备传感器和用户的传感器首选项,可以是正常或反向的纵向方向。 如果用户锁定了基于传感器的旋转,其行为与 portrait 相同,否则,其行为与 sensorPortrait 相同。API 18及以上。 |
sensor | 方向由设备方向传感器决定。显示方向取决于用户如何手持设备,它会在用户旋转设备时发生变化。 但一些设备默认情况下不会旋转到所有四种可能的方向。要允许全部四种方向,请使用 "fullSensor"。 |
fullSensor | 方向由 4 种方向中任一方向的设备方向传感器决定。这与 "sensor" 类似,不同的是它允许所有 4 种可能的屏幕方向,无论设备正常情况下采用什么方向(例如,一些设备正常情况下不使用反向纵向或反向横向,但它支持这些方向)。API 9及以上。 |
nosensor | 决定方向时不考虑物理方向传感器。传感器会被忽略,因此显示不会随用户对设备的移动而旋转。 除了这个区别,系统在选择方向时使用的政策与“unspecified”设置相同。 |
user | 用户当前的首选方向。 |
fullUser | 如果用户锁定了基于传感器的旋转,其行为与 user 相同,否则,其行为与 fullSensor 相同,允许所有 4 种可能的屏幕方向。API 18及以上。 |
locked | 将方向锁定在其当前的任意旋转方向。API 18及以上。 |
在开发中一般用到的属性值也就那么几个,分别是:
unspecified
、landscape
、portrait
,除非有其他需求进行不同的设置。
了解了上面介绍的属性值,下面说一下横竖屏的设置,有两种方式:
- 在AndroidManifest.xml文件中的
<activity></activity>
标签中设置
android:screenOrientation
属性,其属性值根据具体需求可以设置上述表格中介绍的。例如,看视频是设置为横向屏:
<activity
android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- 通过Java代码的方式
上述表格的属性值在代码中与之对应的是ActivityInfo.SCREEN_ORIENTATION_XXX,例如:unspecified
属性值对应的是ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
,具体设置方式如下:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
通过上面的介绍,在开发App时如何设置所有Activity屏幕方向呢?
总结如下:
- 在Application的style中设置
android:screenOrientation
属性,但是通过测试这样是不可行的,因为要求Activity必须在运行中设置才会有效;- 在AndroidManifest.xml配置文件中,针对每个
<activity>
标签设置android:screenOrientation
属性;- 写一个基类
BaseActivity
,在该类中通过Java代码的方式设置,然后让子类Activity继承BaseActivity
。
阻止Activity被重建
我们知道Activity横竖屏切换时他的生命周期会执行完一次,再重新创建一个Activity实例,这个时候相当于是Activity在运行时发生配置更改时,默认情况下会关闭Activity然后将其重新启动,如何阻止Activity被重建,并且会保持运行状态呢?
- 在
<activity>
标签中设置android:configChanges
属性;
下面记录一下该属性的所有属性值。
属性值 | 说明 |
---|---|
mcc | IMSI 移动国家/地区代码 (MCC) 发生了变化 - 检测到了 SIM 并更新了 MCC。 |
mnc | IMSI 移动网络代码 (MNC) 发生了变化 - 检测到了 SIM 并更新了 MNC。 |
locale | 语言区域发生了变化 — 用户为文本选择了新的显示语言。 |
touchscreen | 触摸屏发生了变化。(这种情况通常永远不会发生。) |
keyboard | 键盘类型发生了变化 — 例如,用户插入了一个外置键盘。 |
keyboardHidden | 键盘无障碍功能发生了变化 — 例如,用户显示了硬件键盘。 |
navigation | 导航类型(轨迹球/方向键)发生了变化。(这种情况通常永远不会发生。) |
screenLayout | 屏幕布局发生了变化 — 这可能是由激活了其他显示方式所致。 |
fontScale | 字体缩放系数发生了变化 — 用户选择了新的全局字号。 |
uiMode | 用户界面模式发生了变化 — 这可能是因用户将设备放入桌面/车载基座或夜间模式发生变化所致。API 8及以上。 |
orientation | 屏幕方向发生了变化 — 用户旋转了设备。注意:如果您的应用面向 API 级别 13 或更高级别(按照 minSdkVersion 和 targetSdkVersion 属性所声明的级别),则还应声明 "screenSize" 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变化。 |
screenSize | 当前可用屏幕尺寸发生了变化。它表示当前可用尺寸相对于当前纵横比的变化,因此会在用户在横向与纵向之间切换时发生变化。 不过,如果您的应用面向 API 级别 12 或更低级别,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity)。API 13及以上。 |
smallestScreenSize | 物理屏幕尺寸发生了变化。它表示与方向无关的尺寸变化,因此只有在实际物理屏幕尺寸发生变化(如切换到外部显示器)时才会变化。 对此配置的变更对应于smallestWidth 配置的变化。 不过,如果您的应用面向 API 级别 12 或更低级别,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重新启动 Activity)。API 13及以上。 |
layoutDirection | 布局方向发生了变化。例如,从从左至右 (LTR) 更改为从右至左 (RTL)。 API 17及以上。 |
使用上述表格的属性值时,如果是多个值使用“|”分割,例如:android:configChanges="orientation|keyboardHidden|screenSize"
。
- 配置上述属性,还需要在代码中配合
onConfigurationChanged(Configuration newConfig)
方法,拦截横竖屏变换。
onConfigurationChanged(Configuration newConfig):该方法是当Activity运行时设备配置发生变更时由系统调用。注意。只有当android:configChanges
属性配置的属性值变更时才会调用此函数,如果发生任何没有配置的属性值变更,则系统将停止并重新启动Activity,以使用新配置启动Activity。调用此函数时,将更新资源对象以返回与新配置匹配的资源值(包括视图布局、可绘制对象等),以正确处理变化。
下面看一下,如何使用:
- 首先在AndroidManifest.xml文件中配置
android:configChanges
属性:
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- 在Java代码中,重写
onConfigurationChanged()
方法:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){
Log.i("Alisa","竖屏");
//这里可以针对竖屏做一些处理
}else if(this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
Log.i("Alisa","横屏");
//这里可以针对横屏做一些处理
}
}
其中,getResources().getConfiguration().orientation
可以获取当前的横竖屏。
通过上述操作,可以发现,Activity不会再销毁重新创建一次,而是只调用onConfigurationChanged()
方法,并且会自动保存View中的一些临时数据。
一般情况下,我们在<activity>
标签中配置为:
android:configChanges="orientation|keyboardHidden|screenSize"
注意:API 13及以上,需要配置
screenSize
属性值,否则不起作用。
如果在配置文件中同时配置如下:
<activity
android:name=".MainActivity"
android:screenOrientation="landscape"
android:configChanges="orientation|keyboardHidden|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
此时,不会回调onConfigurationChanged()
方法,但是如果是通过Java代码的形式设置屏幕的方向,会回调onConfigurationChanged()
方法。
扩展
根据上述的横竖屏切换的回调,我们就可以及时知道当前屏幕的状态变化,由于屏幕的横竖屏都有两个不同的方向,所以我们可以通过获取当前屏幕精确的方向。具体如下:
- 获取当前屏幕的旋转角度:
Display display = getWindowManager().getDefaultDisplay();
int rotation = display.getRotation();//当前的屏幕角度
屏幕角度有4个值,以竖屏顺时针旋转,分别是:
- Surface.ROTATION_0:竖屏
- Surface.ROTATION_90:横屏
- Surface.ROTATION_180:反向竖屏
- Surface.ROTATION_270:反向横屏
- 通过获取到的当前屏幕角度同上述4个值做判断,针对具体需求设定屏幕方向。
以上,就是这次整理的关于横竖屏的知识,欢迎大家积极留言,一起探讨学习!也请大家继续关注我的文章!
网友评论