旋转建议

在 Android 8.0 中,用户可以通过“快捷设置”图块或“显示”设置,在自动屏幕旋转模式和纵向旋转模式之间切换。在 Android P 中,我们对纵向旋转模式进行了更新,以便通过固定当前屏幕旋转来避免发生意外旋转(即使设备位置发生变化)。用户可以根据需要手动触发旋转,只需按一下导航栏中的新按钮即可。我们将纵向模式重命名为“旋转锁定”,它会在自动屏幕旋转模式关闭时启用。自动屏幕旋转模式没有任何变化。

当设备处于旋转锁定模式时,用户可以将其屏幕锁定为顶层的可见 Activity 支持的任何旋转方式(假设当前存在系统限制)。如果顶层 Activity 可以在自动屏幕旋转模式下以多种旋转方式呈现,则应在旋转锁定模式下提供相同的选项(根据 Activity 的 screenOrientation 设置,会有一些例外情况)。

旋转锁定模式的工作原理是:当设备旋转方式发生变化时,在导航栏中显示一个按钮。为此,即使自动屏幕旋转模式处于关闭状态,设备的屏幕方向传感器也必须一直保持启用状态。点按此按钮可有效设定用户旋转偏好设置:Settings.System.USER_ROTATION。WindowManager 会使用此偏好设置以及有关顶层 Activity 和系统状态的其他详细信息来更改系统的旋转方式。移动到另一个 Activity 时,在确定以何种旋转方式呈现系统时,WindowManager 会继续使用用户旋转偏好设置。

此 GIF 图片显示了屏幕处于纵向方向的横向放置的手机。出现一个图标,询问用户是否要将屏幕方向更改为横向。
图 1. 启用“在主屏幕按钮上向上滑动”手势后的旋转建议按钮

在多个 Activity 之间移动时,应遵循用户旋转偏好设置。不过,由于大多数手机用户只希望在短时间内暂时处于横屏模式,因此我们添加了自然屏幕方向偏差。只要系统旋转方式更改为设备的自然屏幕方向,用户旋转偏好设置就会重置为设备的自然屏幕方向。对于大多数手机而言,设备的自然屏幕方向是纵向 (0º)。当用户使用仅纵向显示的应用、锁定手机或返回到启动器工作区时,系统通常会重置用户旋转偏好设置。

在过去十年间,用户与屏幕旋转方式的互动没有发生太大变化。鉴于用户之前在导航栏中查找旋转选项和按钮的经历,他们可能会发现此功能难以找到。因此,我们向旋转按钮添加了介绍模式,它此模式会在旋转按钮出现时将其突出显示。介绍模式行为仅发生在用户进行前几次按钮互动时,之后便会停用。

来源

Android P 中已添加对旋转建议的支持。大部分更改都包含在下列文件中。

  • services/.../server/policy/PhoneWindowManager.java
    • 使用 WindowOrientationListener 的输出的钩子(MyOrientationListener,负责监控传感器以确定设备屏幕是否已旋转)
    • 使 WindowOrientationListener 保持有效状态(即使自动屏幕旋转模式已停用)(请参见 needSensorRunningLp()
    • 在考虑用户旋转偏好设置、顶层 Activity screenOrientation 设置和系统状态的情况下,计算系统旋转方式(请参见 rotationForOrientationLw()
    • 确定顶层 Activity 是否可以旋转到指定的旋转状态(请参见 isRotationChoicePossible()
  • SystemUI/.../statusbar/phone/NavigationBarFragment
    • 确定导航栏按钮是否应在接收到来自 PhoneWindowManager 的旋转建议回调时显示(请参见 onRotationProposal()
    • 确定何时隐藏旋转导航栏按钮(请参见对 setRotateSuggestionButtonState(false) 的调用)
    • 处理按钮超时,包括导航栏处于隐藏状态时(通常在全屏模式下)的特殊情况
    • 在返回到设备的自然屏幕方向时重置用户偏好设置 (mRotationWatcher)
    • NavigationBarView 中应用的导航栏按钮动画选择适当样式(请参见 onRotationProposal()
    • 添加介绍模式逻辑,包括专门的动画(请参见对 Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED 的引用)
    • 实现 disable2 旋转标记(请参见 disable()
  • SystemUI/.../statusbar/phone/NavigationBarView.java
    • 设置按钮图标动画样式,使其与将要进行的旋转相匹配(请参见 updateRotateSuggestionButtonStyle()
    • 处理按钮可见性更改(请参见 setRotateButtonVisibility()),其中包括在某些无障碍服务处于有效状态时隐藏旋转按钮的逻辑(用来计算最右侧导航栏按钮堆栈排名)
  • SystemUI/res/layout/menu_ime.xml
    • 为旋转按钮添加新的 KeyButtonView,使其叠加在菜单和 IME/键盘选择器上方,但位于无障碍按钮的下方
  • SystemUI/res/drawable/ic_sysbar_rotate_button.xml
    • 复杂的 AnimatedVectorDrawable,用于为旋转导航栏按钮添加动画效果
    • 样式设置(位于 SystemUI/res/values/styles.xml 中)用于设置旋转的开始角度和结束角度,以便使用相同的可绘制资源,为各种开始和结束角度的旋转添加动画效果
    • 图标的色调调节通过 TintedKeyButtonDrawable 进行设置

实现

Android P 包含了所有必要的更改,以获取适用于使用软件导航键(返回、主屏幕等)的设备的旋转建议。

如果设备制造商打造的设备采用的是硬件导航键且希望实现此功能,则需要设计和实现他们自己的系统界面或停用该功能。当设备保持在与当前系统旋转方向呈 90º 或 180º 的位置且可供快速访问时,建议让任何引入的表面都易于使用。因此,不建议像对 IME/键盘选择器使用通知一样对该功能使用通知。

使用此功能的硬件要求与使用自动屏幕旋转的要求相同。

为确保实现的一致性,当自动屏幕旋转处于关闭状态时,如果系统出于任何原因更改为设备的自然旋转,则用户旋转偏好设置 (Settings.System.USER_ROTATION) 需重置为设备的自然旋转。提供的实现就是这样做的(请参见 NavigationBarFragment.mRotationWatcher)。

StatusBarManager.disable2 中有一个新标记,可暂时阻止旋转建议显示。请参见 StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS。所有实现都必须有此标记,因为关键系统应用(包括设置向导)需要用到此标记。提供的实现支持此标记(请参见 NavigationBarFragment.disable())。

我们强烈建议您启用该功能并遵循 AOSP 实现(如果可能)。我们的目标是确保用户在使用不同的设备时可获得相似的屏幕旋转体验,从而体现出当前大多数手机上提供的自动屏幕旋转和纵向锁定的体验一致性。

自定义

由于旋转建议仅在旋转锁定模式(自动屏幕旋转处于关闭状态)下显示,因此您可以选择让自动屏幕旋转默认处于关闭状态,从而选择是否让该功能在新安装的应用中默认处于开启状态。请参阅 SettingsProvider/res/values/defaults.xml 中的 def_accelerometer_rotation,以进行默认设置更改。

用户可以通过“快捷设置”或“显示”设置中的旋转图块,轻松更改自动屏幕旋转的启用状态(无论默认设置如何)。

验证

对于测试,您可以通过更改门槛 Settings.Secure 值来关闭和开启该功能。要想实现这一点,最简单的方法是从特权 adb 实例运行以下命令:

adb shell settings put secure show_rotation_suggestions <x>

将 x 设置为 0,表示关闭该功能;将其设置为 1,表示开启该功能。

对于测试,您可以通过更改关联的 Settings.Secure 值来重置引入模式。要想实现这一点,最简单的方法是从特权 adb 实例运行以下命令:

adb shell settings put secure num_rotation_suggestions_accepted 0