为可折叠设施和大屏设施优化您的利用
Android 设施的屏幕尺寸突飞猛进,随着平板和可折叠设施的遍及度越来越高,在开发响应式用户界面时,理解您利用的窗口尺寸和状态显得尤为重要。Jetpack WindowManager 现已进入 beta 测试阶段,这个库提供了与 Android 框架中 WindowManager 比拟类似的性能,包含了对反对响应式 UI、检测屏幕扭转的回调适配器和测试窗口 API 的反对。但 Jetpack WindowManager 还新增了对可折叠设施和 ChromeOS 这类窗口环境的反对。
新的 WindowManager API 蕴含了以下内容:
- WindowLayoutInfo: 蕴含了窗口的显示个性,例如该窗口是否可折叠或蕴含铰链
- FoldingFeature: 让您可能监听可折叠设施的折叠状态得以判断设施的姿势
- WindowMetrics: 提供以后窗口或全副窗口的显示指标
Jetpack WindowManager 不与 Android 绑定,这让 API 可能迅速地迭代以反对疾速倒退的市场,还让开发者们可能通过更新库而不用期待 Android 版本更新来取得反对。
当初,Jetpack WindowManager 库已进入 beta 测试阶段,咱们激励所有开发者来应用 Jetpack WindowManager,其与设施无关 API、测试 API 以及它引入的 WindowMetrics,使您的利用可能轻松响应窗口尺寸的变动。曾经进入 beta 测试阶段,意味着您能够安心地专一于在这些设施上打造激动人心的体验,Jetpack WindowManager 最低反对到 API 14。
对于 Jetpack WindowManager
Jetpack WindowManager 是一个以 Kotlin 优先的现代化库,它反对不同状态的新设施,并提供 “ 类 AppCompat” 的性能以构建具备响应式 UI 的利用。
折叠状态
反对可折叠设施是 Jetpack WindowManager 库最直观的性能。当设施的折叠状态变动时,利用将收到相应的事件,进而更新 UI 界面以反对新的用户交互。
△ 在 Samsung Galaxy Z Fold2 上运行的 Google Duo
您能够通过 Google Duo 学习案例 来理解如何反对可折叠设施。
折叠状态有两种,别离是 FLAT (展平) 和 HALF_OPENED (半开)。对于 FLAT,您能够认为外表是齐全平坦关上的,只管有些状况下它有可能被铰链宰割。对于 HALF_OPENED,窗口中有至多两个逻辑区域。咱们在下方用图片说明了每种状态各自可能的状况。
△ 折叠状态: FLAT) 和 HALF-OPENED)
在利用沉闷的状态下,能够通过 Kotlin 数据流收集事件来取得折叠状态扭转的信息。
咱们通过 lifecycleScope 来管制事件收集的开始和完结,正如文章《设计 repeatOnLifeCycle API 背地的故事》和示例代码所述:
lifecycleScope.launch(Dispatchers.Main) {
// 传递给 repeatOnLifecycle 的代码块将在生命周期进入 STARTED 时执行
// 并在生命周期为 STOPPED 时勾销
// repeatOnLifecycle 将会在生命周期再次进入 STARTED 时主动重启代码块
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
// 当生命周期处于 STARTED 时平安地从 windowInfoRepository 中收集数据
// 当生命周期进入 STOPPED 时进行收集数据
windowInfoRepository.windowLayoutInfo
.collect { newLayoutInfo ->
updateStateLog(newLayoutInfo)
updateCurrentState(newLayoutInfo)
}
}
}
当用户能够看到利用时,利用能够应用其接管到的 WindowLayoutInfo 对象中蕴含的信息更新布局。
FoldingFeature 包含了诸如铰链 方向 ),及折叠性能是否创立了两个逻辑屏幕区域 (isSeparating) 属性 ) 这类信息。咱们能应用这些值来查看设施是否处于桌面模式 (屏幕半开并且铰链处于程度方向):
△ 设施处于 TableTop 模式
private fun isTableTopMode(foldFeature: FoldingFeature) =
foldFeature.isSeparating &&
foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
或者书本模式 (屏幕半开并且铰链处于垂直方向):
△ 设施处于 Book 模式
private fun isBookMode(foldFeature: FoldingFeature) =
foldFeature.isSeparating &&
foldFeature.orientation == FoldingFeature.Orientation.VERTICAL
请参阅: 可折叠设施中的桌面模式,文内示例介绍了如何在媒体播放器利用中实现这样的性能。
留神: 在主线程 /UI 线程中收集事件这点非常重要,这能防止在 UI 和事件处理之间的同步问题。
反对响应式 UI
Android 设施的屏幕尺寸变动非常频繁,因而着手设计可能齐全自适应和响应式的 UI 十分重要。Jetpack WindowManager 库中蕴含的另一个性能——可能检索以后窗口和最大窗口的指标信息。这和 API 30 当中的 WindowMetrics API 相似,但它向后兼容到 API 14。
Jetpack WindowManager 提供了两种路径来检索 WindowMetrics 信息,通过数据流事件中的流或者通过 WindowMetricsCalculator 类进行同步解决。
当在编写视图代码时,应用异步 API 可能比拟艰难 (比方 onMeasure)),此时能够应用 WindowMetricsCalculator。
val windowMetrics =
WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)
另一个应用场景是用于测试中 (详见上面的测试一节)。
在解决利用 UI 的高阶用法中,通过该库提供的 WindowInfoRepository#currentWindowMetrics) 可能在窗口尺寸变更时收到告诉,这与是否触发配置变更无关。
这个例子是对于如何依据可用区域来切换您的布局:
// 因为 repeatOnLifecycle 是挂起函数,所以创立一个新的协程
lifecycleScope.launch(Dispatchers.Main) {
// 传递给 repeatOnLifecycle 的代码块将在生命周期进入 STARTED 时执行
// 并在生命周期为 STOPPED 时勾销
// 它将会在生命周期再次进入 STARTED 时主动重启
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
// 当生命周期处于 STARTED 时平安地从 windowInfoRepository 中收集数据
// 当生命周期进入 STOPPED 时进行收集数据
windowInfoRepository.currentWindowMetrics
.collect { windowMetrics ->
val currentBounds = windowMetrics.bounds
Log.i(TAG, "New bounds: {$currentBounds}")
// 咱们能够依据须要在这里更新布局
}
}
}
回调适配器
要在 Java 编程语言中应用这个库或者应用回调接口,请在您的利用中增加 androidx.window:window-java 依赖。该组件提供了 WindowInfoRepositoryCallbackAdapter,您能够通过它注册 (勾销注册) 一个用以接管设施姿势及窗口指标信息更新的回调。
public class SplitLayoutActivity extends AppCompatActivity {
private WindowInfoRepositoryCallbackAdapter windowInfoRepository;
private ActivitySplitLayoutBinding binding;
private final LayoutStateChangeCallback layoutStateChangeCallback =
new LayoutStateChangeCallback();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
windowInfoRepository =
new WindowInfoRepositoryCallbackAdapter(WindowInfoRepository.getOrCreate(this));
}
@Override
protected void onStart() {super.onStart();
windowInfoRepository.addWindowLayoutInfoListener(Runnable::run, layoutStateChangeCallback);
}
@Override
protected void onStop() {super.onStop();
windowInfoRepository.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}
class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
@Override
public void accept(WindowLayoutInfo windowLayoutInfo) {binding.splitLayout.updateWindowLayout(windowLayoutInfo);
}
}
}
测试
开发者们讲到,更强壮的测试 API 对于保护 LTS (长期反对) 是非常要害的。让咱们来聊聊如何在一般设施上测试可折叠设施姿势。
当初,咱们曾经晓得 Jetpack WindowManager 库能够在设施姿势扭转时,向您的利用发送告诉,以便您批改利用的布局。
该库在 androidx.window:window-testing 中提供了 WindowLayoutInfoPublisherRule 让您可能公布一个 WindowInfoLayout
以反对测试 FoldingFeature:
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
咱们能够在测试中虚构一个 FoldingFeature:
val feature = FoldingFeature(
activity = activity,
center = center,
size = 0,
orientation = VERTICAL,
state = HALF_OPENED
)
val expected =
WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()
publisherRule.overrideWindowLayoutInfo(expected)
而后应用 WindowLayoutInfoPublisherRule
来公布它:
val publisherRule = WindowLayoutInfoPublisherRule()
publisherRule.overrideWindowLayoutInfo(expected)
最初,应用可用的 Espresso 匹配器 来查看咱们正在测试的 Activity 的布局是否合乎预期。
上面这个测试中公布了一个处于 HALF_OPENED
状态并且铰链垂直于屏幕核心的 FoldingFeature:
@Test
fun testDeviceOpen_Vertical(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val feature = FoldingFeature(
activity = activity,
orientation = VERTICAL,
state = HALF_OPENED
)
val expected =
WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()
val value = testScope.async {activity.windowInfoRepository().windowLayoutInfo.first()}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
Assert.assertEquals(
expected,
value.await())
}
}
// 查看在有垂直折叠个性时 start_layout 在 end_layout 的左侧
// 这须要在足够大的屏幕上运行测试以适应屏幕上的两个视图
onView(withId(R.id.start_layout))
.check(isCompletelyLeftOf(withId(R.id.end_layout)))
}
查看示例代码
Github 上的 最新示例 展现了如何应用 Jetpack WindowManager 库从 WindowLayoutInfo
流收集信息,或者通过向 WindowInfoRepositoryCallbackAdapter
注册回调来获取显示姿势信息。
该实例还蕴含一些测试,它们能够在任何设施或模拟器中运行。
在您的利用中应用 WindowManager
可折叠设施及双屏设施不再仅仅是实验性的或前瞻的——大屏幕空间和额定的设施姿势曾经被证实是具备用户价值的,而且当初有更多的设施可供您的用户抉择。可折叠设施和双屏设施代表了智能手机的天然进化。对于 Android 开发者来说,这提供了一个进入正在增长的高端市场的机会,感激设施制造商们从新开始关注大屏设施。
咱们去年推出了 Jetpack WindowManager alpha01 版本。该库自那时起开始稳步地倒退,晚期的反馈让其有了很大的改良。当初,它曾经拥抱了 Android 的 Kotlin 优先理念,从回调驱动模型逐步过渡到协程和数据流。随着 WindowManager 进入测试阶段,API 曾经稳固,咱们强烈建议应用它。
更新并不仅限于此。咱们打算为该库增加更多功能,并使其倒退成为与 AppCompat 解绑的零碎 UI 库,使开发者可能在所有的 Android 设施上轻松实现现代化的、响应式的 UI。
- 欢送反馈,让咱们听到您的声音!
- 更多对于为可折叠设施和其它大屏幕设施进行优化的资源,请参阅 这里。
欢迎您 点击这里 向咱们提交反馈,或分享您喜爱的内容、发现的问题。您的反馈对咱们十分重要,感谢您的反对!