Compose 提供了大量基于 Material Design 的可组合项以及 androidx.compose.material:material 依赖项。Material 组件大量使用槽位 API,这是 Compose 引入的一种模式,它在可组合项之上带来一层自定义设置。而Scaffold是槽位的典型,这篇文章会介绍TopAppBar,BottomAppBar,SnackBar,以及Scaffold的使用。
一:TopAppBar
先来看看TopBar的代码
@Composable
fun TopAppBar(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = AppBarDefaults.TopAppBarElevation,
contentPadding: PaddingValues = AppBarDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
){
...
}
- modifier修饰符 Modifier用法详解
- backgroundColor 背景颜色
- contentColor 内容的颜色
- elevation 阴影
- contentPadding 内容边距 可通过PaddingValue设置,也可使用默认AppBarDefaults.ContentPadding
- content 内容控件 比如我们下面举例一个,左边是menu的Icon,右边是标题
@Preview
@Composable
fun topBarView(){
TopAppBar(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
backgroundColor = MaterialTheme.colors.primarySurface,
elevation = 4.dp,
contentPadding = AppBarDefaults.ContentPadding
) {
Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "menu按钮",
tint = Color.White,
modifier = Modifier
.clickable {
//点击了按钮
}
.padding(start = 12.dp, end = 12.dp)
.fillMaxHeight()
)
Text(text = "页面标题",fontSize = 17.sp,color = Color.White)
}
}
}
二:BottomAppBar
先来看看BottomBar的代码
@Composable
fun BottomAppBar(
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colors.primarySurface,
contentColor: Color = contentColorFor(backgroundColor),
cutoutShape: Shape? = null,
elevation: Dp = AppBarDefaults.BottomAppBarElevation,
contentPadding: PaddingValues = AppBarDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
){
...
}
- modifier修饰符 Modifier用法详解
- backgroundColor 背景颜色
- contentColor 内容的颜色
- cutoutShape 形状
- elevation 阴影
- contentPadding 内容边距 可通过PaddingValue设置,也可使用默认AppBarDefaults.ContentPadding
- content 内容控件
比如我们举例,有5个tab
@Preview
@Composable
fun bottomBarView(){
val posState = remember {
mutableStateOf(0)
}
BottomAppBar(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
backgroundColor = Color.White,
contentColor = Color.White,
elevation = 4.dp,
cutoutShape = RoundedCornerShape(4),
contentPadding = AppBarDefaults.ContentPadding
) {
Row() {
// 比如说BottomBar是5个tab
val modifier = Modifier
.fillMaxHeight()
.weight(1f, true)
tabItemView(0,modifier,posState)
tabItemView(1,modifier,posState)
tabItemView(2,modifier,posState)
tabItemView(3,modifier,posState)
tabItemView(4,modifier,posState)
}
}
}
@Composable
fun tabItemView(pos:Int,modifier: Modifier,posState:MutableState<Int>){
Column(
modifier=modifier.clickable {
posState.value = pos
}
,horizontalAlignment = Alignment.CenterHorizontally
,verticalArrangement = Arrangement.Center
) {
val imageVector = when(pos){
0->Icons.Filled.Home
1->Icons.Filled.Message
2->Icons.Filled.Domain
3->Icons.Filled.Favorite
else->Icons.Filled.Person
}
val message = when(pos){
0->"首页"
1->"消息"
2->"建筑"
3->"喜欢"
else->"我的"
}
Icon(imageVector = imageVector, contentDescription = message,tint = if(posState.value == pos) Color.Blue else Color.Black)
Text(text = message,fontSize = 10.sp,color = if(posState.value == pos) Color.Blue else Color.Black)
}
}
三:SnackBar
我们来看看SnackBar的代码
@Composable
fun Snackbar(
modifier: Modifier = Modifier,
action: @Composable (() -> Unit)? = null,
actionOnNewLine: Boolean = false,
shape: Shape = MaterialTheme.shapes.small,
backgroundColor: Color = SnackbarDefaults.backgroundColor,
contentColor: Color = MaterialTheme.colors.surface,
elevation: Dp = 6.dp,
content: @Composable () -> Unit
){...}
- modifier 修饰符
- action 行为的内容是什么控件
- actionOnNewLine 表示action内容是否在新的一行,为true为另为一行,否则会和content的内容叠在一起
- shape形状 默认是MaterialTheme.shapes.small 也就是RoundedCornerShape(4.dp)
- backgroundColor 设置背景颜色
- contentColor 设置内容的颜色
- elevation设置阴影
- content 内容是什么控件
@Preview()
@Composable
fun snackbarTest(){
Snackbar(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
action = {
TextButton(
onClick = {
Log.e("ccm","点击了隐藏按钮")
}) {
Text(text = "隐藏",color = Color.White)
}
},
actionOnNewLine = true,
shape = MaterialTheme.shapes.small,
backgroundColor = SnackbarDefaults.backgroundColor,
contentColor =MaterialTheme.colors.surface,
elevation = 6.dp
) {
Text(text = "提示的信息",color=Color.White,fontSize = 12.sp)
}
}
四:Scaffold
先来看看Scaffold的代码:我们依旧通过一个属性讲解举例,来掌握Scaffold组件
@Composable
fun Scaffold(
modifier: Modifier = Modifier,
scaffoldState: ScaffoldState = rememberScaffoldState(),
topBar: @Composable () -> Unit = {},
bottomBar: @Composable () -> Unit = {},
snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
floatingActionButton: @Composable () -> Unit = {},
floatingActionButtonPosition: FabPosition = FabPosition.End,
isFloatingActionButtonDocked: Boolean = false,
drawerContent: @Composable (ColumnScope.() -> Unit)? = null,
drawerGesturesEnabled: Boolean = true,
drawerShape: Shape = MaterialTheme.shapes.large,
drawerElevation: Dp = DrawerDefaults.Elevation,
drawerBackgroundColor: Color = MaterialTheme.colors.surface,
drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
drawerScrimColor: Color = DrawerDefaults.scrimColor,
backgroundColor: Color = MaterialTheme.colors.background,
contentColor: Color = contentColorFor(backgroundColor),
content: @Composable (PaddingValues) -> Unit
){
...
}
- modifier修饰符 Modifier用法详解
- topBar 顶部的组件。一般是标题栏,比如使用上面讲的TopAppBar。
- bottomBar 底部的组件。比如使用上面讲的BottomAppBar。比如可以是5个Tab
- snackbarHost 其实是设置了一个Snackbar控件。我们来看看具体实现。
@Composable fun SnackbarHost( hostState: SnackbarHostState, modifier: Modifier = Modifier, snackbar: @Composable (SnackbarData) -> Unit = { Snackbar(it) } )
- hostState这个是SnackbarHost状态
- modifier 修饰符
- snackbar Snackbar控件。所以主要还是看Snackbar控件 在具体看看Snackbar(it)点进去的代码
所以其实snackbarHost这个属性就是设置了一个Snackbar控件。只不过把Snackbar的content属性是一个Text文本控件,并且该文本控件的内容是snackbarData的message属性,Snackbar的action是一个TextButton,并且TextButton的行为刚好是执行了snackbarData的performAction,而且该TextButton显示的文本刚好是snackbarData.actionLable属性。再讲一个SnackbarDuration@Composable fun Snackbar( snackbarData: SnackbarData, modifier: Modifier = Modifier, actionOnNewLine: Boolean = false, shape: Shape = MaterialTheme.shapes.small, backgroundColor: Color = SnackbarDefaults.backgroundColor, contentColor: Color = MaterialTheme.colors.surface, actionColor: Color = SnackbarDefaults.primaryActionColor, elevation: Dp = 6.dp ) { val actionLabel = snackbarData.actionLabel val actionComposable: (@Composable () -> Unit)? = if (actionLabel != null) { @Composable { TextButton( colors = ButtonDefaults.textButtonColors(contentColor = actionColor), onClick = { snackbarData.performAction() }, content = { Text(actionLabel) } ) } } else { null } Snackbar( modifier = modifier.padding(12.dp), content = { Text(snackbarData.message) }, action = actionComposable, actionOnNewLine = actionOnNewLine, shape = shape, backgroundColor = backgroundColor, contentColor = contentColor, elevation = elevation ) } // 具体看看SnackbarData是什么 interface SnackbarData { val message: String val actionLabel: String? val duration: SnackbarDuration fun performAction() fun dismiss() }
- duration是设置显示的时长。有三种SnackbarDuration.Long(显示10000L后自动消失),SnackbarDuration.Short(显示4000L后自动消失),SnackbarDuration.Indefinite(显示后不消失,除非点了action按钮隐藏)
- floatingActionButton 是浮动的按钮
- floatingActionButtonPosition 浮动按钮的显示位置。FabPosition取值可以是FabPosition.End 底部居右,一个是FabPosition.Center居中
- isFloatingActionButtonDocked 为true的时候,会有一半的高度遮盖在bottomBar上面
- drawerContent 是左边弹出的drawer组件。类似以前的DrawerLayout
- drawerGesturesEnabled 弹出的左边的drawer组件能否通过手指拖动的形式打开和关闭
- drawerShape drawer组件的形状
- drawerElevation drawer组件的阴影
- drawerBackgroundColor drawer组件的背景颜色
- drawerContentColor drawer组件的内容的颜色
- drawerScrimColor drawer组件打开的时候,右边余留的空间的颜色值
- backgroundColor scaffold的背景颜色
- contentColor scaffold的内容的颜色
- content scaffold的包含的内容
- scaffoldState 设置Scaffold的状态,主要是设置drawerState和snackbarHostState的状态。scaffoldState是一个ScaffoldState,我们具体来看看ScaffoldState的代码
@Stable class ScaffoldState( val drawerState: DrawerState, val snackbarHostState: SnackbarHostState )
- drawerState 设置drawer的控件的状态,比如open,close
- snackbarHostState 这个是设置snackbarHost的状态,比如显示snackbarHost,隐藏snackbarHost 比如我们举例,点击了floatingActionButton按钮显示出snackbarHost控件,点击了topBar的menuIcon。显示出来drawer控件。
@Preview
@Composable
fun scaffoldTest(){
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
val posState = remember {
mutableStateOf(0)
}
Scaffold(
scaffoldState = scaffoldState,
topBar = {
topBarView(scaffoldState)
},
bottomBar = {
bottomBarView(posState)
},
// 这个是SnackbarHost组件
// snackbarHost,
floatingActionButton = {
FloatingActionButton(
onClick = {
scope.launch {
scaffoldState.snackbarHostState.showSnackbar(
"点击了FloatingActionButton",
"隐藏",
duration = SnackbarDuration.Short
)
}
}
) {
Text(text = "Open SnackbarHost",fontSize = 10.sp,color=Color.White,modifier = Modifier.padding(start=10.dp,end = 10.dp))
}
},
floatingActionButtonPosition = FabPosition.End,
// 为true的时候,会有一半的高度遮盖在bottomBar上面
isFloatingActionButtonDocked = false,
drawerContent = {
val modifier = Modifier
.fillMaxWidth()
.padding(start = 20.dp, top = 15.dp, bottom = 15.dp)
Text(text = "drawer title",fontSize = 14.sp,modifier = modifier,textAlign = TextAlign.Left)
Text(text = "drawer 第一个内容",fontSize = 14.sp,modifier=modifier,textAlign = TextAlign.Left)
Text(text = "drawer 第二个内容",fontSize = 14.sp,modifier=modifier,textAlign = TextAlign.Left)
Text(text = "drawer 第三个内容",fontSize = 14.sp,modifier=modifier,textAlign = TextAlign.Left)
},
// 表示能够通过手指拖动的形式打开和关闭drawer控件
drawerGesturesEnabled = true,
drawerShape = RoundedCornerShape(4.dp),
drawerElevation = 20.dp,
drawerBackgroundColor = MaterialTheme.colors.primarySurface,
drawerContentColor = Color.White,
// drawer控件打开的时候,右边剩余的部分的颜色
drawerScrimColor = DrawerDefaults.scrimColor,
backgroundColor = Color.White
// contentColor 内容的颜色
) {
Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {
val text = when(posState.value){
0->"第一个Tab的内容"
1->"第二个Tab的内容"
2->"第三个Tab的内容"
3->"第四个Tab的内容"
else-> "第五个Tab的内容"
}
Text(text = text)
}
}
}
// TopBar
@Composable
fun topBarView(scaffoldState:ScaffoldState){
val scope = rememberCoroutineScope()
TopAppBar(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
backgroundColor = MaterialTheme.colors.primarySurface,
elevation = 4.dp,
contentPadding = AppBarDefaults.ContentPadding
) {
Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "返回按钮",
tint = Color.White,
modifier = Modifier
.clickable {
// 打开drawer
scope.launch {
scaffoldState.drawerState.open()
}
}
.padding(start = 12.dp, end = 12.dp)
.fillMaxHeight()
)
Text(text = "页面标题",fontSize = 17.sp,color = Color.White)
}
}
}
// bottomBar
@Composable
fun bottomBarView(posState:MutableState<Int>){
BottomAppBar(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
backgroundColor = Color.White,
contentColor = Color.White,
elevation = 4.dp,
cutoutShape = RoundedCornerShape(4),
contentPadding = AppBarDefaults.ContentPadding
) {
Row() {
// 比如说BottomBar是5个tab
val modifier = Modifier
.fillMaxHeight()
.weight(1f, true)
tabItemView(0,modifier,posState)
tabItemView(1,modifier,posState)
tabItemView(2,modifier,posState)
tabItemView(3,modifier,posState)
tabItemView(4,modifier,posState)
}
}
}
@Composable
fun tabItemView(pos:Int,modifier: Modifier,posState:MutableState<Int>){
Column(
modifier=modifier.clickable {
posState.value = pos
}
,horizontalAlignment = Alignment.CenterHorizontally
,verticalArrangement = Arrangement.Center
) {
val imageVector = when(pos){
0->Icons.Filled.Home
1->Icons.Filled.Message
2->Icons.Filled.Domain
3->Icons.Filled.Favorite
else->Icons.Filled.Person
}
val message = when(pos){
0->"首页"
1->"消息"
2->"建筑"
3->"喜欢"
else->"我的"
}
Icon(imageVector = imageVector, contentDescription = message,tint = if(posState.value == pos) Color.Blue else Color.Black)
Text(text = message,fontSize = 10.sp,color = if(posState.value == pos) Color.Blue else Color.Black)
}
}