新的实验性查询方式UiMediaScope详见。​一、概念​​基于窗口可用空间而非设备类型来设计自适应布局实现设备无关性和动态适配性避免硬编码以不同形态布局更好的展示内容。将可用的显示区域分别在宽高上划分为紧凑型Compact、中等型Medium和扩展型Expanded由于垂直滚动的普遍性通常根据可用宽度进行适配。窗口 Window系统实际分配给应用的区域而不是物理显示屏。窗口尺寸类别Window Size Classes通过一组稳定的断点对可用宽度和高度进行分类。窗格 Pane一个内容区域。在紧凑窗口中可以单独显示在较大窗口中可以与相关内容并排显示。类型宽度展示内容的窗格数常见设备Compat紧凑型width 6001手机竖屏折叠竖屏展开、竖屏半折叠Medium中等型600 ≤ width 8401推荐或2平板竖屏折叠横屏展开横屏半折叠Expanded扩展型840 ≤ width 12001或2推荐手机横屏平板横屏折叠横屏二分屏电脑Large大型1200 ≤ width 16002或3推荐外接显示器Extra-large超大型1600 ≤ width3或4推荐外接显示器二、手动实现窗口尺寸类别 WindowSizeClasses根据窗口大小自动加载不同布局。如果仅仅是列表详情、辅助窗格的场景使用下方开箱即用的组件会更方便。2.1 添加依赖最新版本implementation(androidx.compose.material3.adaptive:adaptive:1.2.0)启动 Large 和 Extra-large 在调用 API 时设置currentWindowAdaptiveInfo(supportLargeAndXLargeWidth true)2.2 基本使用2.2.1 方式一已过时Composable fun Demo( windowWidthSizeClass: WindowWidthSizeClass currentWindowAdaptiveInfo().windowSizeClass ) { //根据判断结果分别加载不同界面或对变量赋值 when (windowWidthSizeClass) { WindowWidthSizeClass.COMPACT - CompactScreen() WindowWidthSizeClass.MEDIUM - MediumScreen() WindowWidthSizeClass.EXPANDED - ExpandedScreen() else - CompactScreen() } }2.2.2 方式二判断顺序必须从大到小Composable fun Demo( windowSizeClass: WindowSizeClass currentWindowAdaptiveInfo().windowSizeClass ) { //根据判断结果分别加载不同界面或对变量赋值≥返回true if (windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND)) { ExpandedScreen() } else if (windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND)) { MediumScreen() } else { CompactScreen() } }2.3 优化2.3.1 动画添加动画 AnimatedContent 使界面切换更平滑。Composable fun Demo( windowSizeClass: WindowSizeClass currentWindowAdaptiveInfo().windowSizeClass ) { AnimatedContent( targetState windowSizeClass ) { windowSizeClass - if(windowSizeClass...) {...} } }2.3.2 针对APP使用了密度适配的情况密度适配详见UI图给的宽高一般都是根据手机设计的修改密度后作用域内获取 WindowSizeClass 进行屏幕大小判断的结果总是 Compact因此初始化时通过 CompositionLocal 来提供全局获取。//无法提供默认值TopLevel没有Compose作用域 val LocalWindowSizeClass compositionLocalOfWindowSizeClass { error(LocalWindowSizeClass没有默认值) } Composable fun AppTheme() { val windowSizeClass currentWindowAdaptiveInfo().windowSizeClass CompositionLocalProvider( LocalWindowSizeClass provides windowSizeClass ) { content() } }封装进自定义主题写法//通过全局入口AppTheme获取更符合直觉因此设为private private val LocalWindowSizeClass compositionLocalOfWindowSizeClass { error(LocalWindowSizeClass没有默认值) } //声明一个单例用作全局入口 object AppTheme { //用于获取窗口大小信息 val windowSizeClass: WindowSizeClass Composable get() LocalWindowSizeClass.current } Composable fun AppTheme() { val windowSizeClass currentWindowAdaptiveInfo().windowSizeClass CompositionLocalProvider( LocalWindowSizeClass provides windowSizeClass ) { content() } }三、开箱即用列表详情布局、辅助窗格布局Navigation3直接支持目的地可设置成列表或详情自动根据窗口大小切换显示逻辑。详见3.1 列表详情布局 NavigableListDetailPaneScaffold官方页面会根据窗口大小自适应大窗口并排显示列表页和详情页小窗口只显示列表页点击后显示详情页。框架Composablefun T NavigableListDetailPaneScaffold(navigator: ThreePaneScaffoldNavigatorT, //导航器listPane: Composable ThreePaneScaffoldPaneScope.() - Unit, //列表窗格detailPane: Composable ThreePaneScaffoldPaneScope.() - Unit, //详情窗格modifier: Modifier Modifier,extraPane: (Composable ThreePaneScaffoldPaneScope.() - Unit)? null, //额外窗格(提供额外的背景信息)defaultBackBehavior: BackNavigationBehavior BackNavigationBehavior.PopUntilScaffoldValueChange,paneExpansionDragHandle: (Composable ThreePaneScaffoldScope.(PaneExpansionState) - Unit)? null,paneExpansionState: PaneExpansionState? null,)导航器Composablefun T rememberListDetailPaneScaffoldNavigator(scaffoldDirective: PaneScaffoldDirective calculatePaneScaffoldDirective(currentWindowAdaptiveInfo()),adaptStrategies: ThreePaneScaffoldAdaptStrategies ListDetailPaneScaffoldDefaults.adaptStrategies(),isDestinationHistoryAware: Boolean true,initialDestinationHistory: ListThreePaneScaffoldDestinationItemT DefaultListDetailPaneHistory,): ThreePaneScaffoldNavigatorT动画Composablefun S, T : PaneScaffoldValueS ExtendedPaneScaffoldPaneScopeS, T.AnimatedPane(modifier: Modifier Modifier,enterTransition: EnterTransition motionDataProvider.calculateDefaultEnterTransition(paneRole),exitTransition: ExitTransition motionDataProvider.calculateDefaultExitTransition(paneRole),boundsAnimationSpec: FiniteAnimationSpecIntRect PaneMotionDefaults.AnimationSpec,content: (Composable AnimatedPaneScope.() - Unit),)val coroutineScope rememberCoroutineScope() //数据类型实现Parcelable支持保存和恢复所选列表项 val navigator rememberListDetailPaneScaffoldNavigatorMyData() NavigableListDetailPaneScaffold( modifier Modifier.background(AppColors.green), //列表和详情之间会有间隔显示这个颜色 navigator navigator, listPane { AnimatedPane { //可选默认的窗格动画 ListPage( list dataList, onItemClick { coroutineScope.launch { //导航到详情窗格 navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, it) } } ) } }, detailPane { AnimatedPane { //currentDestination是当前目的地contentKey是携带的数据 navigator.currentDestination?.contentKey?.let { it- DetailPage(it) } ?: DetailPage(dataList[0]) //还没有被点击就默认展示第一条数据的详情页 } } )3.2 辅助窗格布局 NavigableSupportingPaneScaffold官方页面