Jetpack Compose: 使用 Chips 的 TextField

4

Text Field with Chips in jetpack compose

我正在尝试实现与照片类似的行为。一个能做同样事情的库也会很有用。 我已经尝试过 https://github.com/dokar3/ChipTextField 但遇到了一些问题。
java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/compose/ui/platform/LocalSoftwareKeyboardController;

我建议你改进你的问题,使用一个[MCVE]。如果你提供一个能够重现问题的示例项目,开发者将更容易帮助你,而不仅仅是看到一个错误信息。 - undefined
请查看https://m3.material.io/components/chips/overview,以获取更多信息。 - undefined
你的模块中启用了Compose吗? - undefined
1个回答

10
你可以使用 FlowRowChipBasicTextField 来实现它。

enter image description here

1- 创建一个数据类,它包含Uri和String的属性。
@Immutable
data class ChipData(
    val uri: Uri,
    val text: String,
    val id: String = UUID.randomUUID().toString()
)

2- 创建自定义芯片,显示图像和字符串。我使用了Coil库来获取Uri中的Painter。
@Composable
private fun MyChip(
    backgroundColor: Color,
    data: ChipData,
    onDeleteClick: () -> Unit
) {
    Chip(
        modifier = Modifier,
        shape = RoundedCornerShape(50),
        enabled = false,
        onClick = {},
        border = BorderStroke(1.dp, Green400.copy(alpha = .9f)),
        colors = ChipDefaults.chipColors(
            disabledBackgroundColor = backgroundColor,
            disabledContentColor = Color.White
        ),
        leadingIcon = {
            Image(
                painter = rememberAsyncImagePainter(data.uri),
                modifier = Modifier
                    .padding(vertical = 4.dp)
                    .size(34.dp)
                    .clip(CircleShape),
                contentScale = ContentScale.FillBounds,
                contentDescription = null
            )
        }
    ) {
        Text(
            text = data.text,
            modifier = Modifier.weight(1f, fill = false),
            overflow = TextOverflow.Ellipsis,
            maxLines = 1
        )
        Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
        Icon(
            modifier = Modifier
                .clip(CircleShape)
                .clickable {
                    onDeleteClick()
                }
                .background(Color.Black.copy(alpha = .4f))
                .size(16.dp)
                .padding(2.dp),
            imageVector = Icons.Filled.Close,
            tint = Color(0xFFE0E0E0),
            contentDescription = null
        )
    }
}

3- 使用FlowRow来对齐芯片,并将一个BasicTextField放在最后一项。
另外,我使用了rememberLauncherForActivityResult来选择图片,你可以将其添加到gradle中。
implementation("com.google.modernstorage:modernstorage-photopicker:1.0.0-alpha06")

如果你想的话,你可以在SAF中使用另一个或默认的图片选择器。
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ChipAndTextFieldLayout(
    modifier: Modifier = Modifier,
    backgroundColor: Color,
    list: List<ChipData> = emptyList(),
    onChipCreated: (ChipData) -> Unit,
    chip: @Composable (data: ChipData, index: Int) -> Unit
) {

    var text by remember {
        mutableStateOf("")
    }

    val focusRequester = remember {
        FocusRequester()
    }

    val keyboardController: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current

    val photoPicker =
        rememberLauncherForActivityResult(PhotoPicker()) { uris ->
            uris.firstOrNull()?.let { uri: Uri ->
                onChipCreated(
                    ChipData(
                        uri = uri,
                        text = text
                    )
                )
                text = ""
                // Open keyboard after new chip is added
                keyboardController?.show()
            }
        }

    LaunchedEffect(Unit) {
        delay(100)
        focusRequester.requestFocus()
    }

    FlowRow(
        modifier = modifier
            .drawWithContent {
                drawContent()
                drawLine(
                    Green400.copy(alpha = .6f),
                    start = Offset(0f, size.height),
                    end = Offset(size.width, size.height),
                    strokeWidth = 4.dp.toPx()
                )
            },
        horizontalArrangement = Arrangement.spacedBy(6.dp)
    ) {

        list.forEachIndexed { index, item ->
            key(item.id) {
                chip(item, index)
            }
        }

        Box(
            modifier = Modifier.height(54.dp)
                // This minimum width that TextField can have
                // if remaining space in same row is smaller it's moved to next line
                .widthIn(min = 80.dp)
                // TextField can grow as big as Composable width
                .weight(1f),
            contentAlignment = Alignment.CenterStart
        ) {
            BasicTextField(
                modifier = Modifier.focusRequester(focusRequester),
                value = text,
                textStyle = TextStyle(
                    fontSize = 20.sp
                ),
                cursorBrush = SolidColor(backgroundColor),
                singleLine = true,
                onValueChange = { text = it },
                keyboardOptions = KeyboardOptions(
                    imeAction = ImeAction.Done
                ),
                keyboardActions = KeyboardActions(
                    onDone = {
                        if (text.isNotEmpty()) {
                            keyboardController?.hide()
                            photoPicker.launch(
                                PhotoPicker.Args(
                                    PhotoPicker.Type.IMAGES_ONLY, 1
                                )
                            )
                        }
                    }
                )
            )
        }
    }
}

使用方法
@Preview
@Composable
private fun ChipSampleAndTextLayoutSample() {

    val backgroundColor = Green400.copy(alpha = .6f)

    val chipDataSnapshotStateList = remember {
        mutableStateListOf<ChipData>()
    }

    ChipAndTextFieldLayout(
        modifier = Modifier.fillMaxWidth().padding(8.dp),
        list = chipDataSnapshotStateList,
        backgroundColor = backgroundColor,
        onChipCreated = {
            chipDataSnapshotStateList.add(it)
        },

        chip = { data: ChipData, index: Int->
            MyChip(backgroundColor, data){
                chipDataSnapshotStateList.removeAt(index)
            }
        }
    )
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接