17、横竖屏切换:锁定横屏、动态切换与资源文件管理
横竖屏切换,这个话题在手机开发里已经聊得很多了。但到了电视和大屏上,情况就完全不一样了。我刚开始做TV端适配时,就踩过这个坑——直接把手机上的横竖屏逻辑搬过来,结果在电视上跑得一塌糊涂。
说白了,电视和大屏设备的使用场景跟手机完全不同。手机你可以横着看视频,竖着刷朋友圈。但电视呢?你见过谁把电视竖过来用的?嗯,基本没有。所以这一章,我们就来聊聊大屏上的横竖屏到底该怎么处理。
核心观点:电视端99%的场景都是横屏。但有些特殊设备(比如带旋转支架的广告屏、竖屏显示器改造的电视墙)确实需要处理竖屏。所以,我们既要学会锁定横屏,也要掌握动态切换的能力。
17.1 锁定横屏:最简单也最稳妥的方案
如果你的应用只跑在标准电视上,那直接锁定横屏就完事了。我个人习惯在AndroidManifest.xml里给Activity加上这么一行:
<activity
android:name=".MainActivity"
android:screenOrientation="landscape"
android:configChanges="orientation|screenSize|screenLayout">
</activity>
这里有两个关键点:
- screenOrientation="landscape":强制横屏显示。注意是"landscape"不是"sensorLandscape",后者会跟随传感器旋转,在电视上反而容易出问题。
- configChanges:这个必须加上。我在项目中遇到过,不加的话,横竖屏切换时Activity会重建,导致画面闪一下黑屏。用户体验极差。
小技巧:如果你用的是Android 12以上的设备,还可以用android:screenOrientation="userLandscape",这样用户可以通过系统设置强制横屏,但应用本身不主动旋转。
17.2 动态切换:什么时候需要?
有些场景下,锁定横屏就不够用了。比如:
- 你的应用要跑在可旋转的广告机上
- 用户把电视竖过来当信息展示屏
- 某些特殊设备(比如车载后排娱乐屏)支持旋转
这时候,我们需要在代码里动态控制屏幕方向。我一般这么写:
// 切换到横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
// 切换到竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// 跟随传感器
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
但要注意,这个方法会触发Activity重建。如果你不想重建,就得配合configChanges一起用。我曾经在一个广告屏项目里,需要根据用户手势动态旋转屏幕,结果每次旋转都重建Activity,导致广告播放中断。后来我改用configChanges + 手动处理布局切换,才解决了这个问题。
警告:在电视上使用setRequestedOrientation时,一定要先检查设备是否支持旋转。有些电视盒子根本不支持竖屏,调用这个方法会直接崩溃。建议先判断:
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 当前是横屏
} else {
// 当前是竖屏
}
17.3 资源文件管理:横竖屏的布局差异
横竖屏切换的核心问题,其实是布局和资源的适配。你想想看,横屏时屏幕宽高比大概是16:9,竖屏时变成了9:16。同样的UI组件,在两种方向下可能完全没法看。
Android的资源文件系统已经帮我们做好了基础支持。你只需要在res目录下创建对应的文件夹:
| 资源类型 | 横屏 | 竖屏 |
|---|---|---|
| 布局文件 | res/layout-land/ | res/layout-port/ |
| 可绘制资源 | res/drawable-land/ | res/drawable-port/ |
| 尺寸值 | res/values-land/ | res/values-port/ |
但这里有个坑:电视端的横竖屏切换,往往伴随着分辨率的变化。比如一个4K电视横着是3840x2160,竖着就变成了2160x3840。这时候光靠-land和-port是不够的,还得结合-sw<N>dp来精确控制。
我个人习惯的做法是:
- 先定义一套基准布局(默认横屏)
- 在
layout-land里放横屏专属布局 - 在
layout-port里放竖屏专属布局 - 如果竖屏布局和横屏差异不大,就用
layout目录下的通用布局,只在values-port里调整尺寸和边距
经验之谈:不要试图用一套布局适配所有方向。我在一个项目里试过用ConstraintLayout的百分比约束来适配,结果横竖屏切换时,有些控件的位置完全错乱了。最后还是老老实实写了两套布局文件。
17.4 代码层面的动态适配
除了资源文件,有时候我们还需要在代码里动态调整UI。比如一个视频播放器,横屏时是全屏的,竖屏时可能变成小窗播放。这时候就需要监听屏幕方向变化:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 横屏模式:全屏播放
enterFullScreenMode();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
// 竖屏模式:小窗播放
enterMiniPlayerMode();
}
}
注意,这个方法只有在AndroidManifest.xml里配置了configChanges时才会被调用。否则Activity会重建,走的是onCreate流程。
小提示:如果你在onConfigurationChanged里做UI调整,记得先调用super.onConfigurationChanged(newConfig)。我曾经漏掉这一行,结果系统资源文件自动切换了,但我的代码逻辑没跟上,导致界面错乱。
17.5 知识体系图
下面这张图总结了横竖屏切换的核心知识点和决策路径:
17.6 实战中的避坑指南
最后,分享几个我在实际项目中踩过的坑:
- 不要依赖传感器:电视遥控器没有陀螺仪,用
sensorLandscape会导致屏幕永远不旋转。我见过有开发者在电视上用了这个属性,结果用户怎么转电视都没反应。 - 注意焦点问题:横竖屏切换后,焦点可能会丢失。记得在
onConfigurationChanged里重新请求焦点。 - 测试所有分辨率:大屏设备的分辨率五花八门,横竖屏切换后,布局可能在某些分辨率下显示异常。建议用模拟器多测几个分辨率组合。
- 动画要平滑:横竖屏切换时,如果有动画,记得用
ViewPropertyAnimator或者Transition来做平滑过渡。直接setVisibility会显得很生硬。
曾经踩过的坑:我在一个教育大屏项目里,横竖屏切换时没有处理好SurfaceView的销毁和重建,结果视频播放器在切换后黑屏了。后来我改成在onConfigurationChanged里手动销毁SurfaceView再重建,才解决了这个问题。如果你也用到了SurfaceView或TextureView,一定要特别注意。
好了,横竖屏切换这块的内容就这些。说白了,电视端大部分情况锁定横屏就够了,但如果你要适配特殊设备,动态切换和资源文件管理就是必须掌握的技能。记住一个原则:先想清楚你的目标设备是什么,再决定用哪种方案。