Android 横屏时禁止输入法全屏

本文详细记录了两种实现输入法在横屏时禁止全屏的方法:一种是通过修改源码,在frameworks/base/core/java/android/inputmethodservice/InputMethodService.java文件中将boolean isFullscreen的判断条件直接修正为false;另一种是在EditText的xml中添加属性android:imeOptions=actionDone|flagNoExtractUi,以隐藏输入法的抽出框。

            项目需求,要求输入法在横屏时禁止全屏。以下记录两种实现方式。


            一是在源码里进行修改。frameworks/base/core/java/android/inputmethodservice/InputMethodService.java

            找到updateFullscreenMode方法,将boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode();直接修正为boolean isFullscreen=false;


           二是在EditText的xml里加上属性 android:imeOptions="actionDone|flagNoExtractUi",其中flagNoExtractUi是指将输入法的抽出框隐藏,当选择中文输入的时候,就会看起来很别扭。目前没有好的解决办法,欢迎交流。



<think> 我们之前已经讨论过在Android 8.1 AOSP上实现类似灵动岛功能的基本步骤。现在用户要求支持横屏模式,这需要我们在原有基础上增加对横屏的适配。以下是详细的实现过程: ### 横屏模式下的灵动岛实现关键点 1. **横屏检测与布局调整** 在`DynamicIslandView`中需要监听屏幕方向变化,并调整动态岛的位置和大小。 2. **横屏下的安全区域计算** 横屏摄像头区域(刘海/挖孔)的位置会发生变化,需要重新计算安全区域。 3. **通知展示逻辑调整** 横屏动态岛的展开方向和内容布局可能需要调整。 --- ### 详细实现步骤 #### 1. 屏幕方向监听 在`DynamicIslandController`中注册方向变化监听: ```java // SystemUI/src/com/android/systemui/dynamicisland/DynamicIslandController.java public void init() { // 注册方向监听 mContext.registerReceiver(mOrientationReceiver, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); } private BroadcastReceiver mOrientationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int rotation = mWindowManager.getDefaultDisplay().getRotation(); boolean isLandscape = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270); mDynamicIslandView.onOrientationChanged(isLandscape); } }; ``` #### 2. 横屏布局调整 修改`DynamicIslandView`以支持横屏: ```java // SystemUI/src/com/android/systemui/dynamicisland/DynamicIslandView.java public void onOrientationChanged(boolean isLandscape) { mIsLandscape = isLandscape; updateLayoutParams(); } private void updateLayoutParams() { LayoutParams params = (LayoutParams) getLayoutParams(); if (mIsLandscape) { // 横屏调整位置到屏幕左上角 params.gravity = Gravity.TOP | Gravity.LEFT; params.width = dpToPx(30); // 横屏高度变宽 params.height = dpToPx(100); // 横屏宽度变高 } else { // 竖屏默认参数 params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; params.width = dpToPx(100); params.height = dpToPx(30); } requestLayout(); } ``` #### 3. 横屏安全区域计算 横屏摄像头区域位置变化,需要重新计算: ```java // 在onAttachedToWindow()中处理 @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateSafeInsets(); } private void updateSafeInsets() { DisplayCutout cutout = getRootWindowInsets().getDisplayCutout(); if (cutout == null) return; if (mIsLandscape) { // 横屏取左侧或右侧的安全区域 List<Rect> boundingRects = cutout.getBoundingRects(); int safeX = 0; for (Rect rect : boundingRects) { if (rect.left == 0) { // 左侧摄像头 safeX = rect.right + dpToPx(10); } else if (rect.right == getScreenWidth()) { // 右侧摄像头 safeX = rect.left - dpToPx(10) - getWidth(); } } setX(safeX); } else { // 竖屏处理(同之前) int safeY = cutout.getBoundingRects().get(0).bottom + dpToPx(10); setY(safeY); } } ``` #### 4. 横屏通知内容布局 创建横屏专用的布局文件`res/layout/dynamic_island_landscape.xml`: ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/landscape_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:maxLines="1" android:ellipsize="end"/> </LinearLayout> ``` 在代码中根据方向切换布局: ```java public void showNotification(String title, String text) { if (mIsLandscape) { if (mLandscapeView == null) { mLandscapeView = LayoutInflater.from(getContext()) .inflate(R.layout.dynamic_island_landscape, this, true); } TextView titleView = mLandscapeView.findViewById(R.id.landscape_title); titleView.setText(title); } else { // 竖屏布局处理 } } ``` --- ### 横屏动画调整 横屏展开动画方向需要变化: ```java public void toggleExpand(boolean expand) { ValueAnimator animator; if (mIsLandscape) { animator = ValueAnimator.ofInt( getHeight(), expand ? dpToPx(200) : dpToPx(100) ); animator.addUpdateListener(anim -> { getLayoutParams().height = (int) anim.getAnimatedValue(); requestLayout(); }); } else { // 竖屏动画(同之前) } animator.start(); } ``` --- ### 兼容性处理 对于Android 8.1(API 27)以下设备,使用传统方向检测: ```java // 替代方案:通过资源配置判断 boolean isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; ``` --- ### 测试建议 1. **模拟器测试** 使用Android Emulator M1 Preview测试横竖屏切换[^2] 2. **真机测试** 在支持横屏的挖孔屏设备上验证位置准确性 --- ### 注意事项 1. **法律风险** 灵动岛是苹果的专利设计,商业产品需谨慎[^3] 2. **第三方应用适配** 横屏模式下可能需要调整第三方通知的显示逻辑 3. **性能优化** 横竖屏切换避免重复创建视图 --- ### 相关问题 1. 如何让动态岛在横屏游戏场景中自动隐藏? 2. 动态岛如何适配折叠屏设备? 3. 在横屏模式下如何实现动态岛的多任务交互? 4. 如何降低动态岛功能对系统性能的影响? [^1]: Phonearena.com消息指出Android和iOS都在互相借鉴中发展成熟 [^2]: Android Emulator M1 Preview可帮助开发者在M1芯片Mac上测试横屏行为 [^3]: 安卓微信暗黑模式适配案例说明系统特性需要软硬件协同支持 </think>### Android 8.1 AOSP实现横屏灵动岛的详细过程 在Android 8.1 AOSP中实现**支持横屏模式的灵动岛功能**,需要重点解决横竖屏切换的布局适配和交互逻辑。以下是完整的实现步骤: --- ### 一、核心实现原理 灵动岛在横屏模式下需要: 1. **动态重定位**:根据屏幕方向调整位置(避开摄像头区域) 2. **布局重构**:横屏使用同的视图尺寸和内容排列 3. **交互适配**:触摸事件坐标转换 4. **动画优化**:横屏专属过渡动画 ```mermaid graph TD A[屏幕旋转事件] --> B{横屏/竖屏判断} B -->|横屏| C[加载横屏布局] B -->|竖屏| D[加载竖屏布局] C --> E[重定位避开摄像头] D --> F[竖屏定位逻辑] E --> G[注册横屏触摸区域] F --> H[竖屏触摸区域] ``` --- ### 二、详细实现步骤 #### 1. 横竖屏检测与布局切换 ```java // SystemUI/src/com/android/systemui/dynamicisland/DynamicIslandController.java public void onConfigurationChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { // 横屏模式 mDynamicIslandView.setLayoutResource(R.layout.dynamic_island_landscape); repositionForLandscape(); } else { // 竖屏模式 mDynamicIslandView.setLayoutResource(R.layout.dynamic_island_portrait); repositionForPortrait(); } mDynamicIslandView.requestLayout(); } ``` #### 2. 横屏定位算法(避开摄像头) ```java // SystemUI/src/com/android/systemui/dynamicisland/DynamicIslandView.java private void repositionForLandscape() { DisplayCutout cutout = getRootWindowInsets().getDisplayCutout(); int safeMargin = dpToPx(10); if (cutout != null) { List<Rect> cutoutRects = cutout.getBoundingRects(); for (Rect rect : cutoutRects) { // 横屏摄像头通常在左侧或右侧 if (rect.left == 0) { // 左侧摄像头 setX(rect.right + safeMargin); } else if (rect.right == getScreenWidth()) { // 右侧摄像头 setX(rect.left - getWidth() - safeMargin); } } } else { // 无摄像头区域则固定在右上角 setX(getScreenWidth() - getWidth() - safeMargin); } setY(safeMargin); } ``` #### 3. 横屏专属布局文件 ```xml <!-- res/layout/dynamic_island_landscape.xml --> <com.android.systemui.dynamicisland.DynamicIslandView android:layout_width="120dp" <!-- 横屏宽度更大 --> android:layout_height="40dp" android:orientation="horizontal"> <!-- 水平排列 --> <ImageView android:id="@+id/icon" android:layout_width="30dp" android:layout_height="30dp"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical"> <!-- 内容垂直排列 --> <TextView android:id="@+id/title" android:maxLines="1" android:ellipsize="end"/> <TextView android:id="@+id/text" android:maxLines="1" android:ellipsize="end"/> </LinearLayout> </com.android.systemui.dynamicisland.DynamicIslandView> ``` #### 4. 横屏触摸事件处理 ```java @Override public boolean onTouchEvent(MotionEvent event) { if (isLandscape()) { // 横屏坐标转换 float rawX = event.getRawX(); float rawY = event.getRawY(); // 将触摸点映射到视图坐标 PointF localPoint = mapToLocalCoordinates(rawX, rawY); if (isInInteractiveArea(localPoint)) { expandIsland(); // 展开灵动岛 return true; } } return super.onTouchEvent(event); } // 坐标映射方法 private PointF mapToLocalCoordinates(float screenX, float screenY) { int[] location = new int[2]; getLocationOnScreen(location); return new PointF( screenX - location[0], screenY - location[1] ); } ``` #### 5. 横屏专属动画 ```java // 横屏展开动画 private void startLandscapeExpandAnimation() { ValueAnimator widthAnim = ValueAnimator.ofInt( getWidth(), dpToPx(300) // 横屏更大展开宽度 ); ValueAnimator heightAnim = ValueAnimator.ofInt( getHeight(), dpToPx(150) // 横屏更高 ); AnimatorSet set = new AnimatorSet(); set.playTogether(widthAnim, heightAnim); set.setDuration(300); set.addUpdateListener(animation -> { getLayoutParams().width = (int) widthAnim.getAnimatedValue(); getLayoutParams().height = (int) heightAnim.getAnimatedValue(); requestLayout(); }); set.start(); } ``` --- ### 三、系统级适配 #### 1. 修改窗口管理器策略 ```java // frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java @Override public void setRotation(int rotation) { // 屏幕旋转通知SystemUI SystemUIProxy.getInstance().onRotationChanged(rotation); } ``` #### 2. 显示区域安全计算 ```java // frameworks/base/core/java/android/view/DisplayCutout.java public Rect getSafeLandscapeRect() { Rect safeRect = new Rect(); for (Rect rect : mBoundingRects) { if (rect.top == 0) { // 顶部区域 safeRect.top = rect.bottom; } } return safeRect; } ``` --- ### 四、横屏特殊场景处理 #### 1. 游戏横屏模式适配 ```java // 检测全屏应用 public boolean isFullscreenAppRunning() { ActivityManager.RunningTaskInfo info = mActivityManager.getRunningTasks(1).get(0); return (info.topActivity != null && (info.configuration.uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_APPLICATION); } // 游戏模式下调整位置 if (isFullscreenAppRunning()) { setVisibility(VISIBLE); // 置于屏幕顶部中央 setX((getScreenWidth() - getWidth()) / 2); setY(dpToPx(5)); } ``` #### 2. 多任务分屏适配 ```java @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { if (isInMultiWindowMode) { // 分屏模式下缩小尺寸 getLayoutParams().width = dpToPx(80); getLayoutParams().height = dpToPx(30); } } ``` --- ### 五、编译与测试 ```bash # 编译SystemUI模块 make SystemUI -j8 # 强制横屏测试 adb shell settings put system accelerometer_rotation 0 adb shell settings put system user_rotation 1 # 90度横屏 ``` > **测试重点**: > 1. 横竖屏切换布局是否正常过渡 > 2. 全屏游戏/视频场景下的位置 > 3. 多任务分屏的行为 > 4. 同挖孔屏设备的兼容性[^1][^4] --- ### 六、兼容性解决方案 | **问题** | **解决方案** | |-------------------------|----------------------------------| | 旧设备无DisplayCutout API | 反射读取`config_mainBuiltInDisplayCutout` | | 横屏坐标系统差异 | 使用`View#getLocationOnScreen()`转换坐标 | | 横屏输入法遮挡 | 监听`WindowInsets`调整位置 | | 低内存设备性能优化 | 使用`RenderThread`异步渲染动画 | --- ### 相关问题 1. 如何在横屏模式下实现灵动岛与应用内容的交互穿透? 2. 灵动岛如何适配Android折叠屏设备的横屏模式? 3. 横屏游戏场景中如何避免灵动岛遮挡游戏操作区域? 4. 如何为第三方应用提供横屏模式下的灵动岛API? > 实现横屏灵动岛需要深入理解Android的显示系统架构,特别是在处理屏幕旋转和异形屏适配需谨慎[^1][^3]。苹果和安卓系统在功能设计上互相借鉴已成常态,但需注意专利边界[^1]。 [^1]: iOS和Android系统互相借鉴已成行业常态 [^2]: Android Emulator M1 Preview可帮助测试横屏行为 [^3]: 分屏功能在安卓设备上更成熟 [^4]: 深色模式适配需系统级支持
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值