Compose 高级组件

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=truefill=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")
        })
    }
}

下拉列表菜单

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"
            }
        }
    }

}
相关推荐
帅次4 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
时空掠影1 个月前
Kotlin compose 实现Image 匀速旋转
android·java·开发语言·ios·kotlin·android jetpack·android-studio
白瑞德1 个月前
Android LiveData的使用和原理分析
android·android jetpack
alexhilton1 个月前
降Compose十八掌之『密云不雨』| Navigation
android·kotlin·android jetpack
白瑞德2 个月前
ViewModel的创建、销毁和恢复
android jetpack
张可2 个月前
开源一个 Compose 图片查看框架ImageViewer,支持多种手势。
android·kotlin·android jetpack
bytebeats2 个月前
Jetpack Compose粘性标题的一种可行实现
android·android jetpack
彬_小彬2 个月前
Android mvvm最佳实践
android·android jetpack
alexhilton2 个月前
降Compose十八掌之『鱼跃于渊』| Gesture Handling
android·kotlin·android jetpack
Wgllss2 个月前
注解处理器在架构,框架中实战应用:MVVM中数据源提供Repository类的自动生成
android·架构·android jetpack