Scaffold
kotlin
@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 @ExtensionFunctionType 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)?
): Unit
Scaffold 是系统提供的一种脚手架,可以快速的搭建一个页面结构,包含
- topBar 通常是一个 TopAppBar
- bottomBar 通常是一个 BottomNavigation
- floatingActionButton 悬浮按钮
- floatingActionButtonPosition 悬浮按钮位置
- isFloatingActionButtonDocked 悬浮按钮是否贴到 bottomBar 上
- drawerContent 侧滑菜单
kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample2() {
val bottomData = listOf("Home", "List", "User")
var bottomNavigationCurrentIndex by remember {
mutableStateOf(0)
}
Scaffold(
topBar = {
TopAppBar(title = { Text(text = "Title") }, navigationIcon = {
Icon(imageVector = Icons.Default.NavigateBefore, contentDescription = null)
}, actions = {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
})
},
bottomBar = {
BottomNavigation() {
bottomData.forEachI ndexed { index, item ->
BottomNavigationItem(
selected = bottomNavigationCurrentIndex == index,
onClick = { bottomNavigationCurrentIndex = index },
icon = {
BadgeBox(badgeContent = {
Text("1")
}) {
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null
)
}
},
label = { Text(item) })
}
}
},
floatingActionButton = {
ExtendedFloatingActionButton(
icon = {
Icon(
imageVector = Icons.Default.SupervisedUserCircle,
contentDescription = null
)
},
text = { Text("Button") },
onClick = { /*TODO*/ })
},
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true
) {
Text("Body Content")
}
}
BackdropScaffold
kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample6() {
val scope = rememberCoroutineScope()
val selection = remember { mutableStateOf(1) }
val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)
LaunchedEffect(scaffoldState) {
scaffoldState.reveal()
}
BackdropScaffold(
scaffoldState = scaffoldState,
appBar = {
TopAppBar(
title = { Text("Backdrop scaffold") },
navigationIcon = {
if (scaffoldState.isConcealed) {
IconButton(onClick = { scope.launch { scaffoldState.reveal() } }) {
Icon(Icons.Default.Menu, contentDescription = "Localized description")
}
} else {
IconButton(onClick = { scope.launch { scaffoldState.conceal() } }) {
Icon(Icons.Default.Close, contentDescription = "Localized description")
}
}
},
actions = {
var clickCount by remember { mutableStateOf(0) }
IconButton(
onClick = {
// show snackbar as a suspend function
scope.launch {
scaffoldState.snackbarHostState
.showSnackbar("Snackbar #${++clickCount}")
}
}
) {
Icon(Icons.Default.Favorite, contentDescription = "Localized description")
}
},
elevation = 0.dp,
backgroundColor = Color.Transparent
)
},
backLayerContent = {
LazyColumn {
items(if (selection.value >= 3) 3 else 5) {
ListItem(
Modifier.clickable {
selection.value = it
scope.launch { scaffoldState.conceal() }
},
text = { Text("Select $it") }
)
}
}
},
frontLayerContent = {
Text("Selection: ${selection.value}")
LazyColumn {
items(50) {
ListItem(
text = { Text("Item $it") },
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = "Localized description"
)
}
)
}
}
}
}
}
BottomSheetScaffold
在 Scaffold 的基础上还可以从底部上拉出菜单
kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ScaffoldSample7() {
val scope = rememberCoroutineScope()
val scaffoldState = rememberBottomSheetScaffoldState()
BottomSheetScaffold(
sheetContent = {
Box(
Modifier.fillMaxWidth().height(128.dp),
contentAlignment = Alignment.Center
) {
Text("Swipe up to expand sheet")
}
Column(
Modifier.fillMaxWidth().padding(64.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Sheet content")
Spacer(Modifier.height(20.dp))
Button(
onClick = {
scope.launch { scaffoldState.bottomSheetState.collapse() }
}
) {
Text("Click to collapse sheet")
}
}
},
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text("Bottom sheet scaffold") },
navigationIcon = {
IconButton(onClick = { scope.launch { scaffoldState.drawerState.open() } }) {
Icon(Icons.Default.Menu, contentDescription = "Localized description")
}
}
)
},
floatingActionButton = {
var clickCount by remember { mutableStateOf(0) }
FloatingActionButton(
onClick = {
// show snackbar as a suspend function
scope.launch {
scaffoldState.snackbarHostState.showSnackbar("Snackbar #${++clickCount}")
}
}
) {
Icon(Icons.Default.Favorite, contentDescription = "Localized description")
}
},
floatingActionButtonPosition = FabPosition.End,
sheetPeekHeight = 128.dp,
drawerContent = {
Column(
Modifier.fillMaxWidth().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Drawer content")
Spacer(Modifier.height(20.dp))
Button(onClick = { scope.launch { scaffoldState.drawerState.close() } }) {
Text("Click to close drawer")
}
}
}
) { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(100) {
Box(
Modifier
.fillMaxWidth()
.height(50.dp)
)
}
}
}
}
Box
kotlin
@Composable
fun Box(modifier: Modifier?): Unit
Box 布局中所有子组件会从左上角开始堆叠在一起。
kotlin
@Composable
fun BoxSample() {
Box {
Box(Modifier.fillMaxSize().background(Color.Cyan))
Box(
Modifier.matchParentSize()
.padding(top = 20.dp, bottom = 20.dp)
.background(Color.Yellow)
)
Box(
Modifier.matchParentSize()
.padding(40.dp)
.background(Color.Magenta)
)
Box(
Modifier.align(Alignment.Center)
.size(300.dp, 300.dp)
.background(Color.Green)
)
Box(
Modifier.align(Alignment.TopStart)
.size(150.dp, 150.dp)
.background(Color.Red)
)
Box(
Modifier.align(Alignment.BottomEnd)
.size(150.dp, 150.dp)
.background(Color.Blue)
)
}
}
BoxWithConstraints
与 Box 布局一样,只是可以在 BoxWithConstraintsScope 中获取到相关信息
- constraints 父控件的约束信息,包含 minWidth、maxWidth、miniHeight、maxHeight 等像素单位数据
- minWidth 最小宽度的 dp 值
- maxWidth 最大宽度的 dp 值
- miniHeight 最小高度的 dp 值
- maxHeight 最大高度的 dp 值
例如,可以根据大小来判断显示不同的界面
kotlin
@Composable
fun BoxWithConstraintsSample() {
BoxWithConstraints(modifier = Modifier.size(100.dp)) {
val rectangleHeight = 100.dp
if (maxHeight < rectangleHeight * 2) {
Box(Modifier.size(50.dp, rectangleHeight).background(Color.Blue))
} else {
Box(Modifier.size(50.dp, rectangleHeight).background(Color.Red))
}
}
}
Column
kotlin
@Composable
inline fun Column(
modifier: Modifier? = Modifier,
verticalArrangement: Arrangement.Vertical? = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal? = Alignment.Start,
content: (@Composable @ExtensionFunctionType ColumnScope.() -> Unit)?
): Unit
Column 布局可以让其子组件呈纵向排列,在 ColumnScope 中可以使用 Modifier.weight 来设置子组件所占比重。
参数
-
verticalArrangement
当 Column 的大小比它的子组件大小之和还要大的话,那可以通过verticalArrangement来设置不同的排列方式
- horizontalAlignment 设置子组件的对齐方式,包括Alignment.Start、Alignment.End、Alignment.CenterHorizontal
kotlin
@Composable
fun ColumnSample() {
Column(
modifier = Modifier
.requiredHeight(100.dp)
.background(Color.White),
) {
Text(
text = "First ", modifier = Modifier
.background(Color.Red)
.weight(0.7f,fill=false)
)
Text("Second", modifier = Modifier
.background(Color.Green)
.weight(0.3f))
}
}
下图可以看到在设置 weight 的时候fill=true
和 fill=false
的区别,默认情况下 fill = true
Row
kotlin
@Composable
inline fun Row(
modifier: Modifier? = Modifier,
horizontalArrangement: Arrangement.Horizontal? = Arrangement.Start,
verticalAlignment: Alignment.Vertical? = Alignment.Top,
content: (@Composable @ExtensionFunctionType RowScope.() -> Unit)?
): Unit
Row 布局可以让其子组件呈横向排列,在 RowScope 中可以使用 Modifier.weight 来设置子组件所占比重。
参数
- horizontalArrangement 设置横向排列方式
- verticalAlignment 设置纵向对齐方式
kotlin
@Composable
fun RowSample() {
Row(
modifier = Modifier
.width(100.dp)
.background(Color.White)
) {
Text("First", modifier = Modifier.background(Color.Red))
Text("Second", modifier = Modifier.background(Color.Green))
}
}
ConstraintLayout
ConstraintLayout 可以让组件相对屏幕或其他同级组件进行布局,可以减少 Row、Column、Box 布局的互相嵌套。
想要在项目中使用ConstraintLayout,必须在 app/build.gradle 中增加信赖
groovy
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"
用法
- 通过 createRefs 或 createRefFor 创建引用,每一个组件需要有一个引用
- 通过 Modifier.constrainAs 关联引用
- 在 constrainAs 的 body 块中使用 linkTo 、centerVerticallyTo等设置约束
- parent 是默认的父组件的引用,可以直接使用
kotlin
@Composable
fun ConstraintLayoutSample() {
var isChecked by remember {
mutableStateOf(false)
}
ConstraintLayout(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.background(Color.Yellow)
) {
val (icon, primaryText, secondlyText, checkBox) = createRefs()
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null,
modifier = Modifier.constrainAs(icon) {
top.linkTo(parent.top)
start.linkTo(parent.start, margin = 8.dp)
bottom.linkTo(parent.bottom)
})
Text(
"Primary Text",
fontSize = 25.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.constrainAs(primaryText) {
start.linkTo(icon.end, margin = 8.dp)
top.linkTo(parent.top)
})
Text("secondly text", modifier = Modifier.constrainAs(secondlyText) {
start.linkTo(primaryText.start)
top.linkTo(primaryText.top)
bottom.linkTo(parent.bottom)
})
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it },
modifier = Modifier.constrainAs(checkBox) {
centerVerticallyTo(parent)
end.linkTo(parent.end)
})
}
}
解耦
在上面的例子中,我们可以看到约束直接写到组件代码之中,这样不容易让这个约束重复利用,因此我们把这些约束代码解耦出来。
所以我们可以使用另一种方案来实现
- 给 ConstraintsLayout 设置 ContraintSet
- 通过设置 layoutId 来关联组件
kotlin
@Composable
fun ConstraintLayoutSample1() {
var isChecked by remember {
mutableStateOf(false)
}
LazyColumn() {
item {
ConstraintLayout(
modifier = Modifier
.height(100.dp)
.fillMaxWidth()
.background(Color.Yellow),
constraintSet = decoupledConstraints()
) {
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null,
modifier = Modifier.layoutId("icon")
)
Text(
"Primary Text",
fontSize = 25.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.layoutId("primaryText")
)
Text("secondly text",
modifier = Modifier.layoutId("secondlyText"))
Checkbox(
checked = isChecked,
onCheckedChange = { isChecked = it },
modifier = Modifier.layoutId("checkBox")
)
}
}
}
}
private fun decoupledConstraints(): ConstraintSet {
return ConstraintSet {
val icon = createRefFor("icon")
val primaryText = createRefFor("primaryText")
val secondlyText = createRefFor("secondlyText")
val checkBox = createRefFor("checkBox")
constrain(icon) {
centerVerticallyTo(parent)
start.linkTo(parent.start, margin = 8.dp)
}
constrain(primaryText) {
start.linkTo(icon.end, margin = 8.dp)
top.linkTo(parent.top)
}
constrain(secondlyText) {
start.linkTo(primaryText.start)
bottom.linkTo(parent.bottom)
top.linkTo(primaryText.bottom)
}
constrain(checkBox) {
centerVerticallyTo(parent)
end.linkTo(parent.end)
}
}
}
这样,如果想重复使用这一组约束,就变得很简单了
Card
属性
kotlin
@Composable
@NonRestartableComposable
fun Card(
modifier: Modifier? = Modifier,
shape: Shape? = MaterialTheme.shapes.medium,
backgroundColor: Color? = MaterialTheme.colors.surface,
contentColor: Color? = contentColorFor(backgroundColor),
border: BorderStroke? = null,
elevation: Dp? = 1.dp,
content: (@Composable () -> Unit)?
): Unit
参数
- backgroudColor
- contentColor
- border
- elevation 抬高
kotlin
@Composable
fun CardSample() {
Card(
modifier = Modifier.padding(8.dp),
backgroundColor = Color.Green,
contentColor = Color.White,
border = BorderStroke(1.dp, Color.Red),
elevation = 10.dp
) {
Text("Cart Content", Modifier.padding(8.dp), color = Color.Red)
}
}
TabRow
属性
kotlin
@Composable
fun TabRow(
selectedTabIndex: Int?,
modifier: Modifier? = Modifier,
backgroundColor: Color? = MaterialTheme.colors.primarySurface,
contentColor: Color? = contentColorFor(backgroundColor),
indicator: (@Composable (tabPositions: List<TabPosition>) -> Unit)? = @Composable { tabPositions ->
TabRowDefaults.Indicator(
Modifier.tabIndicatorOffset(tabPositions[selectedTabIndex])
)
},
divider: (@Composable () -> Unit)? = @Composable {
TabRowDefaults.Divider()
},
tabs: (@Composable () -> Unit)?
): Unit
- selectedTabIndex 当前选中的 Tab 下标
- indicator 设置指示器
- divider 设置底部分割线
- tabs 设置Tab 数组,通常可以设置为 Tab 或 LeadingIconTab
kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun TabRowSample() {
var selectedIndex by remember {
mutableStateOf(0)
}
Column() {
TabRow(
selectedTabIndex = selectedIndex,
contentColor = Color.Yellow,
indicator = {}
) {
LeadingIconTab(
selected = selectedIndex == 0,
text = {
Text(text = "Tab0")
},
icon = {
Icon(
imageVector = Icons.Default.AccountBox,
contentDescription = null
)
},
selectedContentColor = Color.Red,
unselectedContentColor = Color.White,
onClick = { selectedIndex = 0 })
Tab(selected = selectedIndex == 1, text = {
Text(text = "Tab1")
}, onClick = { selectedIndex = 1 })
Tab(selected = selectedIndex == 2, text = {
Text(text = "Tab2")
}, onClick = { selectedIndex = 2 })
}
Text("current index : $selectedIndex")
}
}
ScrollableTabRow
如果想让 TabRow 内容较多时可以滚动的话,请使用ScrollableTabRow
Surface
kotlin
@Composable
@NonRestartableComposable
fun Surface(
modifier: Modifier? = Modifier,
shape: Shape? = RectangleShape,
color: Color? = MaterialTheme.colors.surface,
contentColor: Color? = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp? = 0.dp,
content: (@Composable () -> Unit)?
): Unit
- shape 设置形状:RectangleShape、CircleShape、RoundedCornerShape、CutCornerShape
- border
- elevation
kotlin
@Composable
fun SurfaceSample() {
Column() {
Surface(
modifier = Modifier
.height(100.dp)
.padding(10.dp),
color = Color.Yellow,
shape = CutCornerShape(10.dp),
border = BorderStroke(1.dp, Color.Green),
elevation = 10.dp
) {
Image(
painter = painterResource(id = R.drawable.newbanner4),
contentDescription = null,
)
}
}
}
LazyColumn
属性
kotlin
@Composable
fun LazyColumn(
modifier: Modifier? = Modifier,
state: LazyListState? = rememberLazyListState(),
contentPadding: PaddingValues? = PaddingValues(0.dp),
reverseLayout: Boolean? = false,
verticalArrangement: Arrangement.Vertical? = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal? = Alignment.Start,
flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean? = true,
content: (@ExtensionFunctionType LazyListScope.() -> Unit)?
): Unit
可滚动的带回收机制的 Column
参数
- state 管理列表的状态,可通过这个操作列表滚动
在 LazyListScpoe 中,可以使用以下语句进行操作:
- item 添加单个列表项
- items 添加一组列表项
- itemsIndexed
- stickyHeader 添加粘性头部
kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyColumnSample1() {
val items = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
var height: Dp
with(LocalDensity.current) {
height = 300.toDp()
}
val state = rememberLazyListState()
val scope = rememberCoroutineScope()
LazyColumn(state = state) {
stickyHeader {
Text(
"Header", modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.padding(vertical = 10.dp)
.clickable {
scope.launch { state.animateScrollToItem(2) }
}
)
}
items(items) {
Text(
text = "Column Item :$it", modifier = Modifier
.height(height)
)
Divider()
DisposableEffect(Unit) {
Log.d("====", "effect:$it")
onDispose {
Log.e("====", "onDispose:$it")
}
}
}
}
}
LazyRow
属性
kotlin
@Composable
fun LazyRow(
modifier: Modifier? = Modifier,
state: LazyListState? = rememberLazyListState(),
contentPadding: PaddingValues? = PaddingValues(0.dp),
reverseLayout: Boolean? = false,
horizontalArrangement: Arrangement.Horizontal? = if (!reverseLayout) Arrangement.Start else Arrangement.End,
verticalAlignment: Alignment.Vertical? = Alignment.Top,
flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean? = true,
content: (@ExtensionFunctionType LazyListScope.() -> Unit)?
): Unit
可滚动的且自动回收的 Row
参数
- state 管理列表的状态,可通过这个操作列表滚动
在 LazyListScpoe 中,可以使用以下语句进行操作:
- item 添加单个列表项
- items 添加一组列表项
- itemsIndexed
- stickyHeader 添加粘性头部
kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyRowSample() {
val items = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
LazyRow() {
stickyHeader { Text(text = "11111") }
items(items) {
Text(
text = "Row Item : $it",
modifier = Modifier
.padding(horizontal = 8.dp)
.background(Color.Green)
)
Spacer(modifier = Modifier.width(8.dp))
}
}
}
LazyVerticalGrid
属性
kotlin
@ExperimentalFoundationApi
@Composable
fun LazyVerticalGrid(
cells: GridCells?,
modifier: Modifier? = Modifier,
state: LazyGridState? = rememberLazyGridState(),
contentPadding: PaddingValues? = PaddingValues(0.dp),
verticalArrangement: Arrangement.Vertical? = Arrangement.Top,
horizontalArrangement: Arrangement.Horizontal? = Arrangement.Start,
flingBehavior: FlingBehavior? = ScrollableDefaults.flingBehavior(),
userScrollEnabled: Boolean? = true,
content: (@ExtensionFunctionType LazyGridScope.() -> Unit)?
): Unit
参数
-
Cells 描述单元格展现形式
GridCells.Adaptive:设置单元格的最小尺寸,让内容自适应
GridCells.Fixed:设置单元格的固定尺寸
kotlin
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyVerticalGridSample() {
val datas = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
LazyVerticalGrid(cells = GridCells.Fixed(2)) {
items(datas) {
Text("Grid Item $it")
}
}
}
ListItem
属性
kotlin
@Composable
@ExperimentalMaterialApi
fun ListItem(
modifier: Modifier? = Modifier,
icon: (@Composable () -> Unit)? = null,
secondaryText: (@Composable () -> Unit)? = null,
singleLineSecondaryText: Boolean? = true,
overlineText: (@Composable () -> Unit)? = null,
trailing: (@Composable () -> Unit)? = null,
text: (@Composable () -> Unit)?
): Unit
系统预设列表项组件
参数
- icon 设置左侧图标
- text 主文本
- secondaryText 副文本
- singleLineSecondaryText 副文本是否单行显示
- overlineText 显示在主文本上面的文本
- trailing 设置右侧内容,通常是Icon、CheckBox 或 Switch
kotlin
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun ListItemSample() {
Column {
ListItem(text = { Text("text") }, icon = {
Icon(
imageVector = Icons.Default.BrokenImage,
contentDescription = null,
modifier = Modifier.size(50.dp)
)
}, secondaryText = {
Text("secondaryText")
}, overlineText = {
Text("overlineText")
}, trailing = {
Text("trailing")
})
}
}
DropdownMenu
下拉列表菜单
kotlin
@Composable
fun DropdownMenu(
expanded: Boolean?,
onDismissRequest: (() -> Unit)?,
modifier: Modifier? = Modifier,
offset: DpOffset? = DpOffset(0.dp, 0.dp),
properties: PopupProperties? = PopupProperties(focusable = true),
content: (@Composable @ExtensionFunctionType ColumnScope.() -> Unit)?
): Unit
- expanded 是否展开
- onDismissRequest 取消回调
- offset 设置偏移量
- properties 可以设置 focusable、dismissOnBackPress、dismissOnClickOutside、securePolicy
kotlin
@Composable
fun DropdownMenuSample() {
var expanded by remember {
mutableStateOf(false)
}
Column() {
IconButton(onClick = {
expanded = !expanded
}) {
Icon(imageVector = Icons.Default.PinDrop, contentDescription = null)
}
DropdownMenu(
expanded = expanded, onDismissRequest = {
Log.i("======", "==========")
expanded = false
}, offset = DpOffset(x = 10.dp, y = 10.dp),
properties = PopupProperties(
focusable = true,
dismissOnBackPress = false,
dismissOnClickOutside = false,
securePolicy = SecureFlagPolicy.SecureOn,//设置安全级别,是否可以截屏
)
) {
DropdownMenuItem(onClick = { expanded = false }) {
Text(text = "Menu 0")
}
DropdownMenuItem(onClick = { expanded = false }) {
Text(text = "Menu 1")
}
DropdownMenuItem(onClick = { expanded = false }) {
Text(text = "Menu 2")
}
}
}
}
Dialog
kotlin
@Composable
fun Dialog(
onDismissRequest: () -> Unit,
properties: DialogProperties = DialogProperties(),
content: @Composable () -> Unit
):Unit
用法
kotlin
@Composable
fun DialogSample() {
var showDialog by remember {
mutableStateOf(false)
}
Column() {
Button(onClick = { showDialog = !showDialog }) {
Text("show dialog")
}
if (showDialog) {
Dialog(onDismissRequest = { showDialog = !showDialog }) {
Box(
Modifier
.size(200.dp, 50.dp)
.background(Color.White))
}
}
}
}
AlertDialog
kotlin
@Composable
fun AlertDialogSample() {
var showDialog by remember {
mutableStateOf(false)
}
Column() {
Button(onClick = { showDialog = !showDialog }) {
Text("show dialog")
}
if (showDialog) {
AlertDialog(
onDismissRequest = {
// Dismiss the dialog when the user clicks outside the dialog or on the back
// button. If you want to disable that functionality, simply use an empty
// onCloseRequest.
showDialog = false
},
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
confirmButton = {
TextButton(
onClick = {
showDialog = false
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
showDialog = false
}
) {
Text("Dismiss")
}
}
)
}
}
}
DrawScope
- drawLine 画直线
- color 设置画笔颜色
- start 开始坐标
- end 结束坐标
- cap 设置线段头尾样式,StrokeCap.Round为圆形
- drawRect 画矩形
- color 画笔颜色
- size 矩形大小
- topLeft 偏移左上角的距离
- drawImage 画图片
- topLeft 偏移左上角的距离
- colorFilter 设置颜色滤镜
- blendMode 渲染模式
- drawRoundRect 画圆角矩形
- cornerRadius 圆角大小
- drawCircle 画圆形
- style
- drawOval 画椭圆
- drawArc 画扇形
- startAngle 开始角度
- sweepAngle 展开角度
- useCenter 是否填充
- drawPoints 画点
- pointMode 各种之间的模式,PointMode.Points为加点;Lines为每两点间连线;Polygon所有点连在一起
- drawPath 根据路径绘制
kotlin
@Composable
fun CanvasSample() {
var imageBitmap: ImageBitmap? = null
with(LocalContext.current) {
imageBitmap = ImageBitmap.imageResource(id = R.drawable.newbanner4)
}
Canvas(
modifier = Modifier
.size(200.dp)
.background(Color.LightGray)
) {
drawLine(
Color.Yellow,
start = Offset(x = 0f, y = 10f),
end = Offset(x = 100f, y = 200f),
strokeWidth = 10f,
cap = StrokeCap.Round
)
drawRect(Color.Yellow, size = Size(100f, 100f), topLeft = Offset(x = 10f, y = 0f))
imageBitmap?.let { drawImage(it) }
drawRoundRect(
Color.Green,
size = Size(100f, 100f),
cornerRadius = CornerRadius(20f, 20f),
style = Stroke(width = 10f)
)
drawCircle(Color.Red, style = Stroke(width = 10f))
drawOval(Color.Cyan)
drawArc(
Color.Blue,
startAngle = 0f,
sweepAngle = 30f,
useCenter = false,
style = Stroke(width = 10f)
)
drawPoints(
listOf(Offset(10f, 10f), Offset(20f, 50f), Offset(40f, 60f), Offset(60f, 100f)),
pointMode = PointMode.Lines,
Color.Black,
strokeWidth = 10f,
cap = StrokeCap.Round
)
}
}
想在传统 xml 布局项目中使用 Compose 怎么办?
Compose 工具包已经提供了 androidx.compose.ui.platform.ComposeView
来在 xml 中增加 Compose UI。
只需要在对应的 xml 中使用即可
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Text View From XML" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/composeView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
在 Activity 中通过 findViewById 或者 view-binding 拿到 composeView 组件后,就可以调用 setContent 方法,在方法体中就可以编写 Compose 代码
kotlin
val composeView = findViewById<ComposeView>(R.id.composeView)
composeView.setContent {
Column {
Text(text = "Text From Compose")
Text(text = "Text From Compose")
Text(text = "Text From Compose")
}
}
想在 Compose 中使用 View 怎么办?
同样系统也提供了 AndroidView 来进行这样的操作。在上面的例子中,我们可以这样写
kotlin
val composeView = findViewById<ComposeView>(R.id.composeView)
composeView.setContent {
Column {
Text(text = "Text From Compose")
Text(text = "Text From Compose")
Text(text = "Text From Compose")
AndroidView(factory = {
TextView(it)
}) {
it.apply {
text = "这是从 Compose 里旋转的 xml 布局的 TextView"
}
}
}
}