借助展开的大显示屏和独特的折叠状态,我们能够在可折叠设备上打造全新用户体验。如需让应用具备折叠感知能力,请使用 Jetpack WindowManager 库,它为可折叠设备窗口功能提供了 API surface 例如折叠边和合页如果应用具备折叠感知能力,就能调整其布局,避免将重要内容放在折叠边或合页区域,并将折叠边或合页用作自然分隔符。
了解设备是否支持桌面模式或图书配置等配置 状态可以指导有关支持不同布局或提供 特定功能
窗口信息
Jetpack WindowManager 中的 WindowInfoTracker
接口会公开窗口布局信息。该接口的 windowLayoutInfo()
方法会返回一个 WindowLayoutInfo
数据流,该数据流会将可折叠设备的折叠状态告知您的应用。WindowInfoTracker#getOrCreate()
方法会创建一个 WindowInfoTracker
实例。
WindowManager 支持使用 Kotlin Flow 和 Java 回调收集 WindowLayoutInfo
数据。
Kotlin 数据流
若要开始和停止 WindowLayoutInfo
数据收集,您可以使用可重启的
生命周期感知型协程,其中 repeatOnLifecycle
代码块是
当生命周期至少为 STARTED
时执行,在
生命周期为 STOPPED
。代码块的执行将自动重新开始
当生命周期再次为 STARTED
时。在以下示例中,该代码块会收集并使用 WindowLayoutInfo
数据:
class DisplayFeaturesActivity : AppCompatActivity() {
private lateinit var binding: ActivityDisplayFeaturesBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDisplayFeaturesBinding.inflate(layoutInflater)
setContentView(binding.root)
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(this@DisplayFeaturesActivity)
.windowLayoutInfo(this@DisplayFeaturesActivity)
.collect { newLayoutInfo ->
// Use newLayoutInfo to update the layout.
}
}
}
}
}
Java 回调
回调兼容性层中包含的
借助 androidx.window:window-java
依赖项,您可以
在不使用 Kotlin 数据流的情况下更新 WindowLayoutInfo
。该制品包括
WindowInfoTrackerCallbackAdapter
类,它会调整
WindowInfoTracker
,以支持向
接收 WindowLayoutInfo
更新,例如:
public class SplitLayoutActivity extends AppCompatActivity {
private WindowInfoTrackerCallbackAdapter windowInfoTracker;
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());
windowInfoTracker =
new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this));
}
@Override
protected void onStart() {
super.onStart();
windowInfoTracker.addWindowLayoutInfoListener(
this, Runnable::run, layoutStateChangeCallback);
}
@Override
protected void onStop() {
super.onStop();
windowInfoTracker
.removeWindowLayoutInfoListener(layoutStateChangeCallback);
}
class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
@Override
public void accept(WindowLayoutInfo newLayoutInfo) {
SplitLayoutActivity.this.runOnUiThread( () -> {
// Use newLayoutInfo to update the layout.
});
}
}
}
RxJava 支持
如果您已在使用 RxJava
(2
或 3
版),
您可以利用一些工件来使用
Observable
或 Flowable
在不使用 Kotlin 数据流的情况下收集 WindowLayoutInfo
更新。
由 androidx.window:window-rxjava2
提供的兼容性层和
androidx.window:window-rxjava3
依赖项包含
WindowInfoTracker#windowLayoutInfoFlowable()
和
WindowInfoTracker#windowLayoutInfoObservable()
方法,可让您的
应用以接收 WindowLayoutInfo
更新,例如:
class RxActivity: AppCompatActivity {
private lateinit var binding: ActivityRxBinding
private var disposable: Disposable? = null
private lateinit var observable: Observable<WindowLayoutInfo>
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Create a new observable.
observable = WindowInfoTracker.getOrCreate(this@RxActivity)
.windowLayoutInfoObservable(this@RxActivity)
}
@Override
protected void onStart() {
super.onStart();
// Subscribe to receive WindowLayoutInfo updates.
disposable?.dispose()
disposable = observable
.observeOn(AndroidSchedulers.mainThread())
.subscribe { newLayoutInfo ->
// Use newLayoutInfo to update the layout.
}
}
@Override
protected void onStop() {
super.onStop();
// Dispose of the WindowLayoutInfo observable.
disposable?.dispose()
}
}
可折叠设备显示屏的功能
Jetpack WindowManager 的 WindowLayoutInfo
类会以 DisplayFeature
元素列表的形式提供显示窗口的功能。
FoldingFeature
是一种 DisplayFeature
,它提供了有关可折叠设备显示屏的信息,其中包括:
state
:设备的折叠状态,即FLAT
或HALF_OPENED
orientation
:折叠边或铰链的方向,即HORIZONTAL
或VERTICAL
isSeparating
:折叠边或铰链是否创建了两个逻辑显示区域,即 true 或 false
HALF_OPENED
的可折叠设备始终将 isSeparating
报告为 true
因为屏幕被分成两个显示区域此外,isSeparating
当应用同时跨屏显示时,在双屏设备上始终为 true
屏幕。
FoldingFeature
bounds
属性(继承自 DisplayFeature
)表示折叠功能的边界矩形(如折叠边或铰链)。可以用边界将元素放到与功能相关的屏幕上:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { // Safely collects from WindowInfoTracker when the lifecycle is // STARTED and stops collection when the lifecycle is STOPPED. WindowInfoTracker.getOrCreate(this@MainActivity) .windowLayoutInfo(this@MainActivity) .collect { layoutInfo -> // New posture information. val foldingFeature = layoutInfo.displayFeatures .filterIsInstance<FoldingFeature>() .firstOrNull() // Use information from the foldingFeature object. } } } }
Java
private WindowInfoTrackerCallbackAdapter windowInfoTracker; private final LayoutStateChangeCallback layoutStateChangeCallback = new LayoutStateChangeCallback(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { ... windowInfoTracker = new WindowInfoTrackerCallbackAdapter(WindowInfoTracker.getOrCreate(this)); } @Override protected void onStart() { super.onStart(); windowInfoTracker.addWindowLayoutInfoListener( this, Runnable::run, layoutStateChangeCallback); } @Override protected void onStop() { super.onStop(); windowInfoTracker.removeWindowLayoutInfoListener(layoutStateChangeCallback); } class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> { @Override public void accept(WindowLayoutInfo newLayoutInfo) { // Use newLayoutInfo to update the Layout. List<DisplayFeature> displayFeatures = newLayoutInfo.getDisplayFeatures(); for (DisplayFeature feature : displayFeatures) { if (feature instanceof FoldingFeature) { // Use information from the feature object. } } } }
桌面折叠状态
利用 FoldingFeature
对象中包含的信息,您的应用可以支持桌上模式等折叠状态。在这种模式下,手机置于平面上、铰链处于水平位置,可折叠屏幕处于半开状态。
桌面模式让用户无需拿着手机就能轻松操作手机。桌面模式非常适合观看媒体内容 例如拍照和视频通话。

您可以使用 FoldingFeature.State
和 FoldingFeature.Orientation
确定设备是否处于桌面模式:
Kotlin
fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean { contract { returns(true) implies (foldFeature != null) } return foldFeature?.state == FoldingFeature.State.HALF_OPENED && foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL }
Java
boolean isTableTopPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL); }
确定设备处于桌面模式后,应相应地更新应用布局。对于媒体应用,这通常意味着将播放内容置于 折叠和定位控件以及补充内容, 免触摸观看或收听体验。
在 Android 15(API 级别 35)及更高版本中,无论设备的当前状态如何,您都可以调用同步 API 来检测设备是否支持桌上模式。
该 API 提供了设备支持的折叠状态列表。如果列表包含桌上模式,您可以拆分应用布局以支持该模式,并针对桌上模式和全屏布局对应用界面运行 A/B 测试。
Kotlin
if (WindowSdkExtensions.getInstance().extensionsVersion >= 6) { val postures = WindowInfoTracker.getOrCreate(context).supportedPostures if (postures.contains(TABLE_TOP)) { // Device supports tabletop posture. } }
Java
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { List<SupportedPosture> postures = WindowInfoTracker.getOrCreate(context).getSupportedPostures(); if (postures.contains(SupportedPosture.TABLETOP)) { // Device supports tabletop posture. } }
示例
MediaPlayerActivity
应用:了解如何使用 Media3 Exoplayer 和 WindowManager 创建具备折叠感知能力的视频播放器。使用 Jetpack WindowManager 在可折叠设备上优化相机应用 Codelab:了解如何为摄影应用实现桌面折叠状态。节目 取景器位于上半部分(首屏)和 控件位于下半部分(非首屏)。
图书模式
另一项独特的可折叠功能是图书折叠状态,即设备处于半开状态 合页是垂直的图书模式非常适合阅读电子书。在大屏可折叠设备上以双页版式显示图书,就像装订的书籍一样可以打开,畅享纸质书般的阅读体验。
如果您想捕捉不同的方面,也可用于摄影 无需动手即可拍照。
您可以采用与桌面模式相同的技术实现图书模式。通过 唯一的区别是,代码应检查折叠功能的屏幕方向 垂直而不是水平:
Kotlin
fun isBookPosture(foldFeature : FoldingFeature?) : Boolean { contract { returns(true) implies (foldFeature != null) } return foldFeature?.state == FoldingFeature.State.HALF_OPENED && foldFeature.orientation == FoldingFeature.Orientation.VERTICAL }
Java
boolean isBookPosture(FoldingFeature foldFeature) { return (foldFeature != null) && (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) && (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL); }
窗口大小变化
应用的显示区域可能会因设备配置更改而发生变化, 例如,当设备折叠或展开、旋转或有窗户时 在多窗口模式下调整大小。
借助 Jetpack WindowManager WindowMetricsCalculator
类,您可以
检索当前和最大窗口指标。喜欢这个平台
WindowMetrics
在 API 级别 30 中引入,即 WindowManager
WindowMetrics
:提供窗口边界,但该 API 具有向后兼容性
最低为 API 级别 14。
请参阅使用窗口大小类别。
其他资源
示例
- Jetpack WindowManager:如何使用 Jetpack 的示例 WindowManager 库
- Jetcaster:使用 Compose 实现桌面模式