Compose 组件 - 一些布局类型

发布时间:2026/8/10 16:48:43
Compose 组件 - 一些布局类型 可组合项不指定布局默认是Box堆叠。一、线性布局一维1.1 纵向 Columinline fun Column(modifier Modifier,verticalArrangement Arrangement.Top, //子元素纵向排列规则horizontalAlignment Alignment.Start, //子元素横向排列规则content: Composable ColumnScope.() - Unit)1.1.1 使Column具备滑动能力val scrollState rememberScrollState() Column(Modifier.verticalScroll(scrollState)) { repeat(20) { Text(text aaaaaaa) } }1.2 横向 Rowinline fun Row(modifier: Modifier Modifier,horizontalArrangement: Arrangement.Horizontal Arrangement.Start,verticalAlignment: Alignment.Vertical Alignment.Top,content: Composable RowScope.() - Unit)二、网格布局二维 Grid详见。虽然减少了 Column/Row 嵌套但 Grid 后期改起来灵活性不行子元素多了协调比例会烦死人只在页面正好是个简单的网格比例可以用用属于封装了个细分场景。三、堆叠布局 Box子元素可通过 Modifier.align() 调整位置通过 Modifier.zIndex() 调整叠层顺序数值越大层级越高通过 Modifier.matchParentSize() 在其它子元素测量完成后再匹配Box大小即希望使某个子项与父项Box同样大而不影响Box尺寸。inline fun Box(modifier: Modifier Modifier,contentAlignment: Alignment Alignment.TopStart,propagateMinConstraints: Boolean false,content: Composable BoxScope.() - Unit)四、流式布局 / 弹性布局FlowColumn/FlowRow 更轻量复杂空间分配用 FlexBox。4.1 FlowColumn / FlowRow详见。4.2 FlexBox详见。五、脚手架 Scaffold槽位Slots API会在界面中留出空位让开发者自行填充使用。Scaffold可以为最常见的Material组件提供槽位TopAppBar、BottomAppBar、FloatingActionBar、Drawer使它们得到适当放置且正确的协同工作。fun Scaffold(modifier Modifier,scaffoldState rememberScaffoldState(), //脚手架的状态例如控件抽屉是否打开topBar {}, //顶部标题栏通常是一个TopAppBarbottomBar {}, //底部导航栏通常是一个BottomNavigationsnackbarHost: Composable (SnackbarHostState) - Unit { SnackbarHost(it) }, //用来展示SnackBarfloatingActionButton {}, //悬浮按钮floatingActionButtonPosition FabPosition.End, //悬浮按钮位置isFloatingActionButtonDocked false, //如果存在BottomBar那么是否与BottomBar重叠一半的高度drawerContent null, //抽屉的内容drawerGesturesEnabled true, //否允许手势控制抽屉的打开和关闭drawerShape MaterialTheme.shapes.large, //抽屉的形状drawerElevation DrawerDefaults.Elevation, //抽屉的阴影高度drawerBackgroundColor MaterialTheme.colors.surface, //抽屉的背景色drawerContentColor contentColorFor(drawerBackgroundColor), //抽屉内容的背景色drawerScrimColor DrawerDefaults.scrimColor, //抽屉组件打开时屏幕剩余部分的遮盖颜色backgroundColor MaterialTheme.colors.background, //脚手架的背景色contentColor contentColorFor(backgroundColor), //脚手架内容背景色content: Composable (PaddingValues) - Unit //脚手架的内容)5.1 顶栏 TopBar官方页面一般使用自定义组件或使用应用顶栏 TopAppBar() 组件以实现跟随页面滚动折叠。但是自定义内容图中绿色部分填充不满容器左边有留白去不掉图中红色部分应用底栏 BottomAppBar() 一样有该问题蓝色部分。Composable fun DemoScreen() { Box( modifier Modifier.fillMaxSize(), contentAlignment Alignment.Center ) { Column { TopAppBar( modifier Modifier .fillMaxWidth() .height(15.dp) .background(AppColor.Red), windowInsets WindowInsets(0,0,0,0), colors TopAppBarDefaults.topAppBarColors( containerColor AppColor.Transparent, scrolledContainerColor AppColor.Transparent ), expandedHeight 0.dp, //设不设置效果一样 title { Row(Modifier.fillMaxSize().background(AppColor.Green)) { } } ) BottomAppBar( modifier Modifier .padding(top 50.dp) .fillMaxWidth() .height(15.dp) .background(AppColor.Blue), windowInsets WindowInsets(0,0,0,0), containerColor AppColor.Transparent, contentColor AppColor.Transparent, content { Row(Modifier.fillMaxSize().background(AppColor.Green)) { } } ) } } }TopAppBar()Composablefun TopAppBar(title: Composable () - Unit, //中间标题区域modifier: Modifier Modifier,navigationIcon: Composable () - Unit {}, //左侧导航图标区域actions: Composable RowScope.() - Unit {}, //右侧菜单区域布局是RowexpandedHeight: Dp TopAppBarDefaults.TopAppBarExpandedHeight, //标题栏高度windowInsets: WindowInsets TopAppBarDefaults.windowInsets, //标题栏将遵守的窗口边衬区colors: TopAppBarColors TopAppBarDefaults.topAppBarColors(),scrollBehavior: TopAppBarScrollBehavior? null, //根据页面滚动的响应方式)TopAppBarDefaultsComposablefun topAppBarColors(// 容器颜色containerColor: Color Color.Unspecified,// 折叠后状态栏颜色scrolledContainerColor: Color Color.Unspecified,// 左侧功能区颜色导航图标navigationIconContentColor: Color Color.Unspecified,// 中间标题区颜色标题文字titleContentColor: Color Color.Unspecified,// 右侧功能区颜色菜单图标actionIconContentColor: Color Color.Unspecified,// 副标题颜色subtitleContentColor: Color Color.Unspecified,): TopAppBarColorsTopAppBarScrollBehaviorComposablefun pinnedScrollBehavior(state: TopAppBarState rememberTopAppBarState(),canScroll: () - Boolean { true },): TopAppBarScrollBehavior固定不动。Composablefun enterAlwaysScrollBehavior(state: TopAppBarState rememberTopAppBarState(),canScroll: () - Boolean { true },snapAnimationSpec: AnimationSpecFloat? MotionSchemeKeyTokens.DefaultEffects.value(),flingAnimationSpec: DecayAnimationSpecFloat? rememberSplineBasedDecay(),reverseLayout: Boolean false,): TopAppBarScrollBehavior向上滚动时收起向下滚动时立即展开。Composablefun exitUntilCollapsedScrollBehavior(state: TopAppBarState rememberTopAppBarState(),canScroll: () - Boolean { true },// TODO Load the motionScheme tokens from the component tokens filesnapAnimationSpec: AnimationSpecFloat? MotionSchemeKeyTokens.DefaultEffects.value(),flingAnimationSpec: DecayAnimationSpecFloat? rememberSplineBasedDecay(),): TopAppBarScrollBehavior向上滚动时收起向下滚动必须滚动到列表顶部才会展开。Composable fun TestScreen() { val scrollBehavior TopAppBarDefaults.enterAlwaysScrollBehavior() Column( Modifier .fillMaxSize() .background(AppColor.Red) .nestedScroll(scrollBehavior.nestedScrollConnection) ) { TopAppBar( expandedHeight AppDimen.Height_Category_ArtistItem, scrollBehavior scrollBehavior, windowInsets WindowInsets(0,0,0,0), colors TopAppBarDefaults.topAppBarColors( containerColor AppColor.Transparent, scrolledContainerColor AppColor.Transparent ), title { TextButton({}) { TextPrimary( modifier Modifier.fillMaxWidth().background(AppColor.Blue), text 应用栏 ) } }, navigationIcon { IconButton({}) { Icon( imageVector Icons.Default.Menu, contentDescription 抽屉栏 ) } }, actions { IconButton({}) { Icon( imageVector Icons.Default.AddCircle, contentDescription 添加 ) } IconButton({}) { Icon( imageVector Icons.Default.MoreVert, contentDescription 更多 ) } } ) val sheetState rememberModalBottomSheetState() ModalBottomSheet( onDismissRequest {}, sheetState sheetState, ) { LazyColumn(Modifier.fillMaxWidth()) { items(50) { TextPrimary(哈哈哈) } } } } }5.2 底栏 BottomBar一般使用自定义组件可使用应用底栏 BottomAppBar可响应滑动隐藏但是填充不满见上方顶栏说明官方页面、导航栏 NavigationBar NavigationBarItem官方页面、侧边导航栏 NavigationRail NavigationRailItem 官方页面、之前还有 BottomNavigation BottomNavigationItem。前面这些都是只在固定位置显示可用自适应导航 NavigationSuiteScaffoldCompact在底部Medium和Expande在侧边常显因此不会跟抽屉栏冲突官方页面我的笔记。5.3 抽屉栏 DrawerScafford 在 material 包下可以设置 drawerContent在 material3 包下已经没有。使用 ModalNavigationDrawer官方页面我的笔记。5.4 悬浮按钮 FloatingActionBar5.5 信息条 SnackBar官方页面用作显示在屏幕底部的剪短通知可在不中断用户体验的情况下提供有关反馈和操作。会在几秒后消失还可以通过点击按钮来关闭。确认操作用户删除电子邮件后弹出一个动作条来提示成功并提供撤销选项。网络状态当失去互联网连接后弹出一个动作条来指明处于离线状态。数据提交成功提交后系统会在通知栏中显示更改已成功保存。fun SnackbarHost(hostState: SnackbarHostState,modifier: Modifier Modifier,snackbar: Composable (SnackbarData) - Unit { Snackbar(it) })suspend fun showSnackbar(message: String, //动作条文本内容actionLabel: String? null, //动作按钮名称withDismissAction: Boolean false, //是否显示关闭按钮duration: SnackbarDuration if (actionLabel null) SnackbarDuration.Short else SnackbarDuration.Indefinite //可指定Short、Long、Indefinite): SnackbarResult //返回的结果有两个值SnackbarResult.Dismissed 自动消失、SnackbarResult.ActionPerformed 用户点击了操作按钮​val coroutineScope rememberCoroutineScope() val snackbarHostState remember { SnackbarHostState() } Scaffold( snackbarHost { SnackbarHost( hostState snackbarHostState, // snackbar {} //自定义动作条界面会覆盖下方showSnackbar中设置的内容和点击监听 ) } ) { Box( modifier Modifier.padding(it).fillMaxSize(), contentAlignment Alignment.Center ) { Button( onClick { coroutineScope.launch { snackbarHostState.showSnackbar( message 动作条文本内容, actionLabel 操作按钮, withDismissAction true, //是否显示关闭按钮 duration SnackbarDuration.Short //Short、Long、Indefinite ).let { when (it) { //动作条自动消失 SnackbarResult.Dismissed - {} //用户点击了操作按钮 SnackbarResult.ActionPerformed - {} } } } } ) { Text(弹出) } } }六、约束布局 ConstraintLayout废弃玩意下面官方页面都打不开了不要再用了早期 Compose 功能不完善用来过渡的能实现的功能都有更好的替代。最新版本implementation androidx.constraintlayout:constraintlayout-compose:1.0.1各种对齐或嵌套过深的时候使用。需要注意以下几点通过 createRefs( ) 或 createRefFor( ) 为子元素创建引用相当于创建ID。通过 Modifier.constranAs( ) 将组合函数和引用绑定在Lambda中指定约束条件。约束条件通过 linkTo( ) 或其它有用的方法指定。parent 是现有的引用可用于指定对 ConstraintLayout 本身的约束条件。默认尽可能小的容纳完子元素wrap_content设置子元素位置gravity依据的是内容宽高若要相对于屏幕位置需要对 ConstraintLayout 使用 Modifier.fillMaxWidth() 或 fillMaxHeight() 或fillMAxSize()。默认允许子元素超出屏幕宽度要设置Dimension.preferredWrapContent。Composable inline fun ConstraintLayout( modifier: Modifier Modifier, optimizationLevel: Int Optimizer.OPTIMIZATION_STANDARD, crossinline content: Composable ConstraintLayoutScope.() - Unit )fun Modifier.constrainAs( ref: ConstrainedLayoutReference, //指定绑定的引用 //提供现有参数start、top、end、bottom表示当前组合函数的上下左右四边调用linkTo()指定该边的约束 constrainBlock: ConstrainScope.() - Unit //设置约束条件 )fun linkTo( //指定连接到哪里parent指ConstraintLayout如 anchor: ConstraintLayoutBaseScope.***Anchor, margin: Dp 0.dp, goneMargin: Dp 0.dp )​Composable fun MyConstraintLayout() { ConstraintLayout { //为每个子元素创建引用 val (buttonRef, textRef) createRefs() Button( onClick { /* Do something */ }, //将组合函数和引用关联 modifier Modifier.constrainAs(buttonRef) { //指定约束条件现有引用parent是指ConstraintLayout top.linkTo(parent.top, margin 16.dp) } ) { Text(Button) } Text( text Text, Modifier.constrainAs(textRef) { top.linkTo(buttonRef.bottom, margin 16.dp) //centerAround(button1.end) //自己中间对齐谁的哪边 centerHorizontallyTo(parent) //水平居中 } ) } }6.1 栅栏 Barrier​Composable fun MyBarrier() { ConstraintLayout { val (button1, button2, text) createRefs() Button(onClick { }, modifier Modifier.constrainAs(button1) { top.linkTo(parent.top, margin 16.dp) }) { Text(text Button1) } Text(text Text, modifier Modifier.constrainAs(text) { top.linkTo(button1.bottom, margin 16.dp) centerAround(button1.end) //自己中间对齐谁的哪边 }) val barrier createEndBarrier(button1, text) //将button1和text组合成一个Barrier Button(onClick { }, modifier Modifier.constrainAs(button2) { top.linkTo(parent.top, margin 16.dp) start.linkTo(barrier) }) { Text(text Button2) } } }6.2 基准线 GuidelinecreateGuidelineFromTop(offset: Dp)createGuidelineFromBottom(offset: Dp)createGuidelineFromStart(offset: Dp)createGuidelineFromEnd(offset: Dp)上下左右基于父布局的偏移量createGuidelineFromTop(fraction: Float)createGuidelineFromBottom(fraction: Float)createGuidelineFromStart(fraction: Float)createGuidelineFromEnd(fraction: Float)上下左右基于父布局的百分比createGuidelineFromAbsoluteLeft(offset: Dp)createGuidelineFromAbsoluteLeft(fraction: Float)createGuidelineFromAbsoluteRight(offset: Dp)createGuidelineFromAbsoluteRight(fraction: Float)国际化Dimension.preferredWrapContent布局大小根据内容设置并受布局约束影响。Dimension.wrapContent 默认值布局大小只根据内容设置不受布局约束影响。Dimension.fillToConstraints布局大小将展开填充由布局约束所限制的空间。Dimension.preferredValue布局大小是一个默认值并受布局约束影响。Dimension.value布局大小是一个默认值不受布局约束影响。Dimension.preferredWrapContent.adLast(100.dp)Dimension.preferredWrapContent.atMost(100.dp)可组合设置布局大小设置最大/最小布局大小。​Composable fun MyGuideline() { ConstraintLayout { val text createRef() val guideline createGuidelineFromStart(fraction 0.5f) Text( text Hellow Wodr!Hellow Wodr!Hellow Wodr!Hellow Wodr!Hellow Wodr!Hellow Wodr!Hellow Wodr!, modifier Modifier.constrainAs(text) { linkTo(start guideline, end parent.end) //默认允许子元素超出屏幕所以这里要设置一下 width Dimension.preferredWrapContent } ) } }6.3 链 Chainfun createHorizontalChain(vararg elements: ConstrainedLayoutReference, //需要打包在一起的子元素引用chainStyle: ChainStyle ChainStyle.Spread //链的类型)fun createVerticalChain(vararg elements: ConstrainedLayoutReference,chainStyle: ChainStyle ChainStyle.Spread)ChainStyle.Spread默认所有子元素平均分布在父布局中ChainStyle.SpreadInside头尾子元素分布在两端其余平均分布剩下空间。ChainStyle.Packed所有子元素打包在一起放在链条中间。​Composable fun MyChain() { ConstraintLayout(modifier Modifier.fillMaxSize()) { val (box1, box2, box3) createRefs() createHorizontalChain(box1, box2, box3, chainStyle ChainStyle.Spread) Box(modifier Modifier.size(100.dp).background(Color.Red).constrainAs(box1) {}) Box(modifier Modifier.size(100.dp).background(Color.Green).constrainAs(box2) {}) Box(modifier Modifier.size(100.dp).background(Color.Blue).constrainAs(box3) {}) } }6.4 约束集解耦 ConstraintSet上面约束都是写在子元素里的可以抽取出来在外部定义通过传参的方式解耦即动态约束。创建 ConstraintSet定义引用和对应的约束条件传给 ConstraintLayout。子元素通过 Modifier.layourId() 设置为 ConstraintSet 中对应的引用。​Composable fun Before() { val margin 16.dp ConstraintLayout { val (button, text) createRefs() Button(onClick {}, modifier Modifier.constrainAs(button) { top.linkTo(parent.top, margin margin) }) { Text(text Button) } Text(text Text, modifier Modifier.constrainAs(text) { top.linkTo(button.bottom, margin margin) }) } } //外部定义约束集 private fun myContraints(margin: Dp) ConstraintSet { //创建引用 val button createRefFor(id button) val text createRefFor(id text) //配置约束条件 constrain(button) { top.linkTo(parent.top, margin) } constrain(text) { top.linkTo(button.bottom, margin) } } Composable fun after() { //该组合项提供的现有参数可用来根据可用空间调用不同的可组合项屏幕适配 //单位dp的参数minWidth、minHeight、maxWidth、maxHeight //单位px的参数constraints.maxWidth BoxWithConstraints { //根据竖屏横屏返回不同的值 val margin if (maxWidth maxHeight) myContraints(16.dp) else myContraints(20.dp) //传入约束集使用 ConstraintLayout(margin) { //子元素设置约束集中对应的引用 Button(onClick {}, modifier Modifier.layoutId(button)) { Text(text Button) } Text(text Text, modifier Modifier.layoutId(text)) } } }