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

}
相关推荐
开发者阿伟2 天前
Android Jetpack DataBinding源码解析与实践
android·android jetpack
alexhilton7 天前
Android技巧:学习使用GridLayout
android·kotlin·android jetpack
Wgllss15 天前
轻松搞定Android蓝牙打印机,双屏异显及副屏分辨率适配解决办法
android·架构·android jetpack
alexhilton21 天前
群星闪耀的大前端开发
android·kotlin·android jetpack
一航jason1 个月前
Android Jetpack Compose 现有Java老项目集成使用compose开发
android·java·android jetpack
帅次1 个月前
Android CoordinatorLayout:打造高效交互界面的利器
android·gradle·android studio·rxjava·android jetpack·androidx·appcompat
IAM四十二1 个月前
Jetpack Compose State 你用对了吗?
android·android jetpack·composer
Wgllss1 个月前
那些大厂架构师是怎样封装网络请求的?
android·架构·android jetpack
x0242 个月前
Android Room(SQLite) too many SQL variables异常
sqlite·安卓·android jetpack·1024程序员节
alexhilton2 个月前
深入理解观察者模式
android·kotlin·android jetpack