Jetpack Compose: 自定义单选按钮组以实现分段按钮

huangapple go评论69阅读模式
英文:

Jetpack compose: customize radio button group to achieve segmented buttons

问题

iOS有类似这样的分段控件。我想在Android中使用Jetpack Compose实现这个,我检查了一下,发现Jetpack Compose中没有内置的类似控件,而且Material 3中的UI目前也不支持Jetpack Compose。我该怎么办?完全自定义单选按钮吗?我知道我可以添加背景和文本等内容,但如何隐藏选中标记,让单选按钮看起来像按钮呢?或者有没有可以用来实现类似分段控件UI的库?有人能给我一些建议吗?

英文:

iOS has Segmented controls like this.Jetpack Compose: 自定义单选按钮组以实现分段按钮
I wanna have this in Android by using jetpack compose and I checked there is not build-in like this, and this UI in material 3 doesn't support jetpack compose now. What can I do? Totally customize the radio button? I know I can add the background and text and something, but how can I just hide check mark, make radio button looks like a button? Or there is something library I can use to use UI like Segmented controls? Can anyone give me some hints?

答案1

得分: 6

你可以使用一个Row布局,将OutlinedButton应用一个Offset来避免双重边框。

类似这样:

Row(
    modifier = Modifier
        .fillMaxWidth()
) {

    val cornerRadius = 16.dp
    var selectedIndex by remember { mutableStateOf(-1) }

    itemsList.forEachIndexed { index, item ->

        OutlinedButton(
            onClick = { selectedIndex = index },
            modifier = when (index) {
                0 ->
                    Modifier
                        .offset(0.dp, 0.dp)
                        .zIndex(if (selectedIndex == index) 1f else 0f)
                else ->
                    Modifier
                        .offset((-1 * index).dp, 0.dp)
                        .zIndex(if (selectedIndex == index) 1f else 0f)
            },
            shape = when (index) {
                0 -> RoundedCornerShape(
                    topStart = cornerRadius,
                    topEnd = 0.dp,
                    bottomStart = cornerRadius,
                    bottomEnd = 0.dp
                )
                itemsList.size - 1 -> RoundedCornerShape(
                    topStart = 0.dp,
                    topEnd = cornerRadius,
                    bottomStart = 0.dp,
                    bottomEnd = cornerRadius
                )
                else -> RoundedCornerShape(
                    topStart = 0.dp,
                    topEnd = 0.dp,
                    bottomStart = 0.dp,
                    bottomEnd = 0.dp
                )
            },
            border = BorderStroke(
                1.dp, if (selectedIndex == index) {
                    Blue500
                } else {
                    Blue500.copy(alpha = 0.75f)
                }
            ),
            colors = if (selectedIndex == index) {
                ButtonDefaults.outlinedButtonColors(
                    containerColor = Blue500.copy(alpha = 0.1f),
                    contentColor = Blue500
                )
            } else {
                ButtonDefaults.outlinedButtonColors(
                    containerColor = MaterialTheme.colorScheme.surface,
                    contentColor = Blue500
                )
            }
        ) {
            Text("Button " + item)
        }
    }
}

Jetpack Compose: 自定义单选按钮组以实现分段按钮

英文:

You can use a Row of OutlinedButton applying an Offset to avoid the double border.

Something like:

    Row(
        modifier = Modifier
            .fillMaxWidth()
    ) {

        val cornerRadius = 16.dp
        var selectedIndex by remember { mutableStateOf(-1) }

        itemsList.forEachIndexed { index, item ->

            OutlinedButton(
                onClick = { selectedIndex = index },
                modifier = when (index) {
                    0 ->
                        Modifier
                            .offset(0.dp, 0.dp)
                            .zIndex(if (selectedIndex == index) 1f else 0f)
                    else ->
                        Modifier
                            .offset((-1 * index).dp, 0.dp)
                            .zIndex(if (selectedIndex == index) 1f else 0f)
                },
                shape = when (index) {
                    0 -> RoundedCornerShape(
                        topStart = cornerRadius,
                        topEnd = 0.dp,
                        bottomStart = cornerRadius,
                        bottomEnd = 0.dp
                    )
                    itemsList.size - 1 -> RoundedCornerShape(
                        topStart = 0.dp,
                        topEnd = cornerRadius,
                        bottomStart = 0.dp,
                        bottomEnd = cornerRadius
                    )
                    else -> RoundedCornerShape(
                        topStart = 0.dp,
                        topEnd = 0.dp,
                        bottomStart = 0.dp,
                        bottomEnd = 0.dp
                    )
                },
                border = BorderStroke(
                    1.dp, if (selectedIndex == index) {
                        Blue500
                    } else {
                        Blue500.copy(alpha = 0.75f)
                    }
                ),
                colors = if (selectedIndex == index) {
                    ButtonDefaults.outlinedButtonColors(
                        containerColor = Blue500.copy(alpha = 0.1f),
                        contentColor = Blue500
                    )
                } else {
                    ButtonDefaults.outlinedButtonColors(
                        containerColor = MaterialTheme.colorScheme.surface,
                        contentColor = Blue500
                    )
                }
            ) {
                Text("Button " + item)
            }
        }
    }

Jetpack Compose: 自定义单选按钮组以实现分段按钮

答案2

得分: 0

以下是翻译好的代码部分:

@Composable
fun SegmentedControl(
    modifier: Modifier = Modifier,
    items: List<String>,
    defaultSelectedItemIndex: Int = 0,
    useFixedWidth: Boolean = false,
    itemWidth: Dp = 120.dp,
    cornerRadius: Int = 24,
    onItemSelection: (selectedItemIndex: Int) -> Unit
) {
    val selectedIndex = remember { mutableStateOf(defaultSelectedItemIndex) }
    val itemIndex = remember { mutableStateOf(defaultSelectedItemIndex) }

    Card(
        modifier = Modifier
            .fillMaxWidth()
            .height(38.dp),
        colors = CardDefaults.cardColors(
            containerColor = if (selectedIndex.value == itemIndex.value) {

                MaterialTheme.colorScheme.background
            } else {

                MaterialTheme.colorScheme.secondary
            }
        ),
        shape = RoundedCornerShape(cornerRadius)
    ) {
        Row(
            modifier = modifier
                .fillMaxWidth()
                .background(MaterialTheme.colorScheme.secondary),
            horizontalArrangement = Arrangement.Center
        ) {
            items.forEachIndexed { index, item ->
                itemIndex.value = index
                Card(
                    modifier = modifier
                        .weight(1f)
                        .padding(2.dp),
                    onClick = {
                        selectedIndex.value = index
                        onItemSelection(selectedIndex.value)
                    },
                    colors = CardDefaults.cardColors(
                        containerColor = if (selectedIndex.value == index) {
                            MaterialTheme.colorScheme.background
                        } else {
                            MaterialTheme.colorScheme.secondary
                        },
                        contentColor = if (selectedIndex.value == index)
                            MaterialTheme.colorScheme.scrim
                        else
                            MaterialTheme.colorScheme.onSecondary
                    ),
                    shape = when (index) {
                        0 -> RoundedCornerShape(
                            topStartPercent = cornerRadius,
                            topEndPercent = cornerRadius,
                            bottomStartPercent = cornerRadius,
                            bottomEndPercent = cornerRadius
                        )

                        items.size - 1 -> RoundedCornerShape(
                            topStartPercent = cornerRadius,
                            topEndPercent = cornerRadius,
                            bottomStartPercent = cornerRadius,
                            bottomEndPercent = cornerRadius
                        )

                        else -> RoundedCornerShape(
                            topStartPercent = 0,
                            topEndPercent = 0,
                            bottomStartPercent = 0,
                            bottomEndPercent = 0
                        )
                    },
                ) {
                    Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center){
                        TitleText(
                            text = item,
                            style = LocalTextStyle.current.copy(
                                fontSize = 14.sp,
                                fontWeight = if (selectedIndex.value == index)
                                    LocalTextStyle.current.fontWeight
                                else
                                    FontWeight.Normal,
                                color = if (selectedIndex.value == index)
                                    MaterialTheme.colorScheme.scrim
                                else
                                    MaterialTheme.colorScheme.onSecondary
                            ),
                            textAlign = TextAlign.Center,

                        )
                    }
                }
            }
        }
    }
}

希望这有所帮助。如果您需要进一步的帮助,请随时告诉我。

英文:

If you want to be exactly the same as the swiftUI component, you can use this code:

Jetpack Compose: 自定义单选按钮组以实现分段按钮

@Composable
fun SegmentedControl(
modifier: Modifier = Modifier,
items: List&lt;String&gt;,
defaultSelectedItemIndex: Int = 0,
useFixedWidth: Boolean = false,
itemWidth: Dp = 120.dp,
cornerRadius: Int = 24,
onItemSelection: (selectedItemIndex: Int) -&gt; Unit
) {
val selectedIndex = remember { mutableStateOf(defaultSelectedItemIndex) }
val itemIndex = remember { mutableStateOf(defaultSelectedItemIndex) }
Card(
modifier = Modifier
.fillMaxWidth()
.height(38.dp),
colors = CardDefaults.cardColors(
containerColor = if (selectedIndex.value == itemIndex.value) {
MaterialTheme.colorScheme.background
} else {
MaterialTheme.colorScheme.secondary
}
),
shape = RoundedCornerShape(cornerRadius)
) {
Row(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondary),
horizontalArrangement = Arrangement.Center
) {
items.forEachIndexed { index, item -&gt;
itemIndex.value = index
Card(
modifier = modifier
.weight(1f)
.padding(2.dp),
onClick = {
selectedIndex.value = index
onItemSelection(selectedIndex.value)
},
colors = CardDefaults.cardColors(
containerColor = if (selectedIndex.value == index) {
MaterialTheme.colorScheme.background
} else {
MaterialTheme.colorScheme.secondary
},
contentColor = if (selectedIndex.value == index)
MaterialTheme.colorScheme.scrim
else
MaterialTheme.colorScheme.onSecondary
),
shape = when (index) {
0 -&gt; RoundedCornerShape(
topStartPercent = cornerRadius,
topEndPercent = cornerRadius,
bottomStartPercent = cornerRadius,
bottomEndPercent = cornerRadius
)
items.size - 1 -&gt; RoundedCornerShape(
topStartPercent = cornerRadius,
topEndPercent = cornerRadius,
bottomStartPercent = cornerRadius,
bottomEndPercent = cornerRadius
)
else -&gt; RoundedCornerShape(
topStartPercent = 0,
topEndPercent = 0,
bottomStartPercent = 0,
bottomEndPercent = 0
)
},
) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center){
TitleText(
text = item,
style = LocalTextStyle.current.copy(
fontSize = 14.sp,
fontWeight = if (selectedIndex.value == index)
LocalTextStyle.current.fontWeight
else
FontWeight.Normal,
color = if (selectedIndex.value == index)
MaterialTheme.colorScheme.scrim
else
MaterialTheme.colorScheme.onSecondary
),
textAlign = TextAlign.Center,
)
}
}
}
}
}
}

huangapple
  • 本文由 发表于 2023年3月4日 02:52:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75630852.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定