[android学习1]横竖屏切换的机理

本文详细解析了手机横竖屏切换的工作原理。从phonewindowmanager注册监听器开始,到sensor触发屏幕方向改变,最终通知Activity进行配置更新。涉及WMS、AMS等组件交互流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为最近遇到了几个奇怪的bug,所以下定决心把横竖屏切换的机制搞清楚,现在和大家分享下:



首先是phonewindowmananger里会注册windoworientationlistener,当sensor有事件上报后,会调用onOrientationChanged,继续调用WMS里的接口setRotation。
在setRotation里会对config进行判断,如果发生了变化就会通知AMS,sendNewConfiguration。
AMS会调用WMS的接口computeNewConfiguration来对新的config进行判断,并在updateConfigurationLocked调用activitythread的scheduleConfigurationChanged,给APPTHREAD发CONFIGURATION_CHANGED;调用ensureActivityConfigurationLocked,紧接着在activitystack里调用app.thread.scheduleActivityConfigurationChanged来给APPTHREAD发送ACTIVITY_CONFIGURATION_CHANGED消息。
到这里,我们的activitythread就会收到并处理这2个关键的消息,遍历当前的callstack,调用各个activity的onconfigrationchange。

调用图如下:
具体怎么切横屏的这个涉及到layout我还没搞清楚。。。

<think>好的,我现在需要帮助用户了解Android中的WindowInsets的用法以及如何处理相关的问题。首先,我需要回忆一下WindowInsets的基本概念。WindowInsets主要用于处理应用窗口与系统UI(比如状态栏、导航栏、输入法键盘等)之间的交互,确保应用内容不会被这些系统UI覆盖。 接下来,我需要考虑用户可能的实际应用场景,比如全屏模式下如何处理系统栏的遮挡,或者输入法弹出时如何调整布局。用户可能希望知道如何正确获取和监听WindowInsets的变化,以及如何应用这些信息来调整界面布局。 我记得在Android中,从API 20(Android 4.4)开始,WindowInsets的获取和处理方式发生了变化,特别是从API 30开始,引入了新的方法来处理手势导航。因此,需要注意不同版本的兼容性问题。例如,使用ViewCompat.setOnApplyWindowInsetsListener可以帮助兼容旧版本。 然后,我需要考虑具体的代码示例。比如,如何通过设置监听器来获取Insets,并调整视图的内边距。例如,在处理系统栏时,可能需要获取statusBars和navigationBars的inset,并将这些值应用到根布局的padding中,以避免内容被遮挡。 另外,全屏模式的处理也是一个常见问题。用户可能希望应用全屏显示,但仍需要处理系统栏的显示和隐藏。这时候,可以使用SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION等标志,并结合View.setOnApplyWindowInsetsListener来调整布局。 输入法处理方面,当软键盘弹出时,需要调整界面布局,比如将EditText上推。可以通过设置Window的softInputMode为adjustResize,并在监听器中处理imeInsets的变化,调整底部视图的位置。 还有,用户可能需要了解如何自定义处理WindowInsets的分发,例如在自定义ViewGroup中,重写onApplyWindowInsets方法,并根据需要分配Insets给子视图,比如只将系统状态栏的inset应用到顶部视图,而将导航栏的inset应用到底部视图。 在总结部分,我需要列出常见问题及解决方法,如兼容性处理、全屏模式、输入法遮挡、手势导航适配等。同时,生成相关问题,帮助用户进一步深入学习,比如处理Fragment中的Insets、与CoordinatorLayout的结合使用,以及隐藏状态栏的正确方法。 最后,确保所有代码示例使用正确的格式,并引用相关文档或最佳实践。例如,引用Android官方文档或开发者指南中的建议,确保回答的准确性。</think>### Android WindowInsets 使用指南 WindowInsets 用于处理应用窗口与系统 UI(如状态栏、导航栏、输入法键盘)的交互,确保内容不被遮挡。以下是关键用法和常见问题解决方案: --- #### 1. 基础用法:监听 Insets 变化 ```kotlin ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> val statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top val navBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom // 调整视图内边距 view.updatePadding(top = statusBarHeight, bottom = navBarHeight) WindowInsetsCompat.CONSUMED } ``` - **作用**:动态响应系统栏高度变化(如横竖屏切换)[^1] - **注意**:需使用 `WindowInsetsCompat` 兼容 API 20+ --- #### 2. 全屏模式处理 ```kotlin window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) view.updatePadding(left = systemBars.left, right = systemBars.right) WindowInsetsCompat.CONSUMED } ``` - **场景**:全屏显示但仍需避开系统栏 - **关键标志**:`LAYOUT_STABLE` 保持布局稳定 --- #### 3. 输入法处理 ```xml <!-- AndroidManifest.xml --> <activity android:windowSoftInputMode="adjustResize" /> ``` ```kotlin ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom bottomView.translationY = -imeHeight // 上推底部视图 WindowInsetsCompat.CONSUMED } ``` - **联动机理**:`adjustResize` 触发 Insets 变化监听 --- #### 4. 手势导航适配(API 30+) ```kotlin WindowCompat.setDecorFitsSystemWindows(window, false) // 全屏边缘手势 ViewCompat.setOnApplyWindowInsetsListener(rootView) { view, insets -> val systemGestures = insets.getInsets(WindowInsetsCompat.Type.systemGestures()) // 处理手势区域边距 view.updatePadding(left = systemGestures.left, right = systemGestures.right) WindowInsetsCompat.CONSUMED } ``` --- #### 5. 自定义 Insets 分发 ```kotlin class CustomLayout : ViewGroup { override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets { val statusBarInset = insets.systemWindowInsetTop val navBarInset = insets.systemWindowInsetBottom // 分配 insets 到子视图 getChildAt(0).dispatchApplyWindowInsets(insets.replaceSystemWindowInsets( insets.systemWindowInsetLeft, 0, // 顶部子视图不处理状态栏 insets.systemWindowInsetRight, insets.systemWindowInsetBottom )) return insets } } ``` --- ### 常见问题解决方案 | 问题类型 | 解决方法 | |---------|----------| | **兼容性问题** | 使用 `WindowInsetsCompat` 代替原生 API | | **全屏内容遮挡** | 结合 `LAYOUT_STABLE` 标志和 Insets 监听 | | **键盘遮挡输入框** | `adjustResize` + 监听 IME 类型 Insets | | **手势冲突** | 处理 `systemGestures` 类型 Insets | --- §§ 相关问题 §§ 1. 如何在 Fragment 中正确处理 WindowInsets? 2. WindowInsets 与 CoordinatorLayout 如何协同工作? 3. 使用 WindowInsets 时如何正确隐藏状态栏? 4. 不同 Android 版本对 WindowInsets 的实现有哪些差异? [^1]: Android 开发者文档 - WindowInsets 最佳实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值