ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Android去除旋转按钮小图标:三种场景与完整解决方案

Android去除旋转按钮小图标:三种场景与完整解决方案 做Android开发的大概率都被这个不起眼的小图标折磨过——测试机上开着自动旋转把手机横过来看视频屏幕边上突然弹出一个旋转按钮或者自己做的播放器页面控制栏上莫名其妙多了个旋转图标产品经理截图过来问这是什么能不能去掉。去掉旋转按钮小图标这个需求搜索下来很简单但真正动手才发现它牵扯到系统UI、Activity方向锁定、控件样式好几层东西方案完全不一样。这篇文章把我实际踩过的坑和不同场景的解法整理了一遍给正在处理同类需求的朋友做个参考。1. 先搞清楚你说的旋转小图标到底是哪一个三种场景的区分很多人搜去掉旋转按钮小图标但实际想解决的问题可能完全不同。如果一上来就照着别人的代码改大概率是做无用功。我至少遇到过三种情况对应完全不同的处理方式第一种是系统悬浮旋转按钮。手机开启自动旋转后在横屏界面里屏幕右下角或边缘会出现一个旋转屏幕的小图标点一下能在横竖屏之间切换几秒后自动消失。这是Android 10之后加入的系统级UI属于SystemUI的一部分。第二种是播放器或者页面控制栏上的旋转按钮。比如VideoView自带控制器、各种三方播放器SDK控制栏上会放一个旋转/全屏切换的图标有的在右上角有的在底部工具栏。这个属于应用内的View开发者可以控制它的显示和隐藏。第三种是加载控件里的旋转动画。ProgressBar默认的圆形加载圈、下拉刷新组件里的刷新圆圈严格来说不是按钮但它在视觉上也是一个旋转的小图标。很多人搜这个关键词其实是想去掉加载圈。场景出现位置性质典型触发条件系统悬浮旋转按钮屏幕角落底部/侧边SystemUI开启自动旋转 设备方向改变播放器/控制栏旋转图标播放器工具栏、页面控制栏应用内View进入全屏或切换横竖屏加载旋转圈页面中央、按钮内部应用内View异步加载中indeterminate模式还有一类冷门情况部分手机ROM在横屏使用输入法时导航栏附近也会弹出一个旋转按钮那是输入法工具栏自带的应用层基本控制不了一般直接忽略。这几种场景的解决方案差异很大。下面按场景逐一说明。2. 系统悬浮旋转按钮应用层能做什么不能做什么这个按钮的官方名称在不同的ROM里不一样但机制是同一套Android 10开始在SystemUI里实现当设备方向发生变化且当前Activity允许方向自适应时系统会显示一个临时悬浮按钮让用户快速切换方向。关键点在于它属于系统进程应用拿不到它的View实例也没有任何公开API能直接对它调用setVisibility(View.GONE)。网上有人提供通过无障碍服务模拟点击或者遍历窗口的方式去隐藏实测非常不稳定系统版本一变就失效而且无障碍服务需要用户手动授权体验也很割裂。所以应用层的正解不是去掉这个按钮而是让这个按钮没有出现的条件。SystemUI只在当前Activity允许方向变化时才显示它一旦界面方向被锁定系统就认为方向是固定的自然不会弹出来。做法一在Manifest里锁定方向。activity android:name.MainActivity android:screenOrientationportrait android:configChangesorientation|screenSize|keyboardHidden /这里有个容易栽的坑screenOrientation的值必须是小写portrait不是Portrait。写成大写在小部分编译环境下不报错但运行时不生效排查起来相当迷惑。做法二代码里动态设置方向。适合某些页面需要根据业务逻辑决定锁横屏还是锁竖屏的场景。override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 注意在setContentView之前调用 requestedOrientation ActivityInfo.SCREEN_ORIENTATION_PORTRAIT setContentView(R.layout.activity_main) }我建议能放在Manifest里就不要放代码里。Manifest的声明在Activity启动时就会生效不会出现先闪一下竖屏再跳转的割裂感。代码方式适合那种同一个Activity复用多种布局、需要动态切换方向的场景。还有一种情况你的App确实需要横屏播放视频但又不想让系统弹出那个旋转按钮。思路一样——播放器进入全屏时把Activity锁定为横屏setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)退出全屏时再恢复为SCREEN_ORIENTATION_UNSPECIFIED。这样整个横屏期间系统悬浮按钮都会被禁用。3. 播放器和控制栏里的旋转图标找到归属再动手这个场景我遇到的最多。业务方看到播放器控制栏上有个旋转图标就提工单说我们不需要这个功能去掉。这个图标往往是应用内控件控制权在你自己手里但还是分几种情况。先判断图标属于谁。用Android Studio的Layout Inspector打开当前界面点一下那个图标如果能在控件树里定位到它就说明它是应用内View可以从布局或者代码里处理。如果在控件树里找不到、只出现在系统层那就要回到上一章的思路。这个判断方法比看代码全局搜索快得多也应该成为排查的第一步。如果图标来自VideoView的MediaController标准MediaController其实没有旋转按钮只有播放/暂停、快进/快退、进度条。但很多项目会在MediaController或自定义Controller里额外加一个全屏/旋转按钮。找到对应的ImageView或Button设置visibilitygone即可ImageButton android:idid/btn_rotate android:layout_width48dp android:layout_height48dp android:srcdrawable/ic_rotate android:visibilitygone /如果用的三方播放器比如GSYVideoPlayer、JiaoZiVideoPlayer、Bilibili的ijkplayer封装大部分都提供了配置开关。以GSY为例// 关闭自动旋转 videoPlayer.setRotateViewAuto(false) // 去掉全屏/旋转按钮图标 videoPlayer.setEnlargeImageRes(0)不同库的命名不一样但思路是共通的要么在XML里隐藏对应id的控件要么在初始化代码里把按钮资源设为0或者直接调用库提供的隐藏旋转按钮方法。不知道具体方法名的时候去源码里搜rotate或者enlarge关键词基本都能找到。还有一种情况旋转按钮来自Toolbar的菜单项。这个最简单直接在menu资源里删掉对应的item或者代码里menu.findItem(R.id.action_rotate).setVisible(false)。很少见但确实存在的一种布局里用了AnimatedVectorDrawable作为旋转图标的动画源代码里通过visibility控制显隐但因为你同时在其他页面的loading动画里也复用了这个drawable资源所以不敢随便删资源文件。这种情况下不要删矢量资源本身用visibility控制当前页面的显示即可这样最安全不影响其他页面的复用。4. 加载旋转圈ProgressBar的去留隐藏、替换还是换风格这个其实跟旋转按钮不完全是一回事但因为搜索热词里出现了android进度条很多人的真实意图其实就是去掉转圈的加载动画所以我专门说一下。ProgressBar在indeterminatetrue模式下默认显示的是一个圆形旋转的加载动画。这就是大多数人说的转圈。如果只是不想让它显示在XML里设置ProgressBar android:idid/loading android:layout_width48dp android:layout_height48dp android:visibilityinvisible /或者代码里binding.loading.visibility View.GONE注意invisible和gone的区别invisible在布局里占位置但不显示gone完全不占位置。大部分场景建议用gone不然页面上会留一个空白区域。还有一种需求不是去掉转圈而是把圆形旋转的加载样式改成水平进度条。这个在下载场景里很常见ProgressBar style?android:attr/progressBarStyleHorizontal android:layout_widthmatch_parent android:layout_heightwrap_content android:indeterminatetrue /如果需求是我不要那个转圈我自己画一个静态的加载图可以用setIndeterminateDrawable替换默认动画Drawableval drawable ContextCompat.getDrawable(this, R.drawable.ic_custom_loading) progressBar.indeterminateDrawable drawable注意这里有个细节setIndeterminateDrawable设置的Drawable如果本身带旋转动画一样会转。如果你要的是一个完全静止的图标就用一个普通的静态Drawable不要去用RotateDrawable或者AnimatedVectorDrawable。按钮内部自带的旋转小圈是另一个容易忽略的场景。很多第三方库比如一些LoadingButton组件在按钮进入loading状态后会在按钮文字左边显示一个转圈。这个通常是组件的属性控制的按组件文档关闭即可比如设置为app:iconGravitytextStart、不传loading图标之类。本质上它也是一个ProgressBar只是被封装在按钮组件内部了。5. 为什么设置了还是会出现旋转按钮一次完整的排查链路光给结论不聊排查过程容易吃亏因为我设置了但没生效才是这个问题里最折磨人的部分。我之前做一个视频播放功能时播放Activity在Manifest里明确设置了screenOrientationportrait结果真机横过来屏幕照样转了系统悬浮按钮也出现了。当时排查了很久最后整理出几条链路按顺序检查基本能定位到问题。第一步确认目标Activity确实被设置了方向锁定。这里有个常见遗漏页面其实有两个Activity入口比如一个MainActivity一个PlayerActivityMainActivity设置了竖屏PlayerActivity是单独的Manifest里忘了设置。我那次就是这个原因——视频播放是单独的一个Activity不管主页面锁得多死那个页面没锁一切白搭。有个比较笨但有效的方法用aapt查看APK里实际打包的Manifest配置aapt dump xmltree app-debug.apk AndroidManifest.xml | grep -A 3 activity不过现在Android Studio打包的APK路径比较隐蔽更方便的做法是直接用Manifest编辑器搜索activity节点一一核对。第二步检查代码里有没有人在运行期动态修改方向。很多播放器库在进入全屏时会主动调用setRequestedOrientation(LANDSCAPE)这会覆盖Manifest里的声明。第三方SDK尤其常见因为它的默认设计就是用户点击全屏按钮后自动转横屏。这时候有两个选择。找到SDK的开关把自动旋转关闭如果SDK不提供这个开关可以重写Activity的setRequestedOrientation方法拦截override fun setRequestedOrientation(requestedOrientation: Int) { if (allowOrientationChange) { super.setRequestedOrientation(requestedOrientation) } else { super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) } }这个方法比较粗暴但实测在某些SDK不配合的场景里是有效的。不过我建议不到万不得已别用因为它会干扰SDK内部的旋转逻辑可能导致播放器画面适配出问题。第三步检查是不是对android:configChanges有误解。configChangesorientation|screenSize|keyboardHidden只表示旋转时不重建Activity不代表禁止旋转。很多文章在介绍怎么锁屏时混着写这两个属性导致不少人以为配置了configChanges就不会旋转了。这是错的。要锁方向一定得有screenOrientationconfigChanges只是辅助。第四步排查是否触发了系统层的强制横屏。部分ROM尤其是国产ROM在控制中心里提供了一个强制横屏或全部应用横屏的全局开关开启后应用自己锁定的方向会被系统覆盖。这种情况下应用层没有代码能绕过。如果测试的时候发现怎么改代码都没用先怀疑这个开关。第五步多窗口和自由窗口模式下要特别留意。在分屏或自由窗口状态下部分系统会忽略screenOrientation因为系统认为窗口需要自适应才能配合多窗口布局。这是系统设计限制不是bug。如果你的需求是多窗口模式下也要锁方向只能从业务逻辑上规避比如检测多窗口状态时提示用户退出分屏。6. 一些经验和判断先定位控制权再动手做多了这类去掉小图标的需求之后我最大的体感就是不要一上来就找代码先判断这个图标到底归谁管。如果在布局文件里能找到控件id那就是应用内的好办改布局或者代码就行。如果在Layout Inspector的控件树里找不到那就是系统UI或SDK非法绘制的有些SDK用WindowManager直接往Window上addView不走布局文件这种就要回到SDK配置或者系统层面想办法。还有个很实用的排查技巧开启开发者选项里的显示布局边界。打开之后页面上每个View都会画出蓝色边框。如果那个旋转图标的边框和应用内某个控件重叠说明它是应用内绘制的如果图标边框悬浮在所有View之外基本就是系统UI叠加层应用层动不了它。另外说个实际测试的细节方向锁定的代码在模拟器上和在真机上表现不完全一致。Google自家的模拟器对screenOrientation处理比较规范但一些ROM的深度定制版可能存在延迟或者特殊逻辑。所以不要只在模拟器上看效果就提交了至少拿一台主流国产ROM的真机跑一遍横竖屏切换再下结论。如果你只是不想让测试机在自己开发的App里弹出系统旋转按钮最快的做法是去系统设置里把自动旋转关掉但如果是给用户做App你需要的是在代码层锁方向。很多人在群里问怎么去掉旋转按钮其实是分不清这两个场景。如果产品需求明确要求这个页面保持竖屏那Manifest里锁方向就够了如果需求是布局要能旋转但别弹按钮这个在Android目前的标准API下没有完美的解法只能锁方向绕开这是平台限制不是代码能力问题。最后再分享一个小技巧如果产品只是不想要旋转按钮但又要保留横竖屏自适应能力可以考虑用SensorLandscape、UserPortrait这类更细致的方向模式结合业务场景在一个Activity内部做横竖屏逻辑切换而不是依赖多个Activity跳转。这样代码上能少踩很多方向覆盖的坑。
返回列表