Jetpack Compose实现类似传统的 Spinner 组件

由于之前的开发习惯,使用 Compose 开发 UI 时想使用 Spinner 组件,就选择使用 AndroidView 加载 Spinner,但是测试发现有些手机上有时候下拉列表不能滚动,数据更新与选中事件的关联也会导致数据出错,可能再花点时间也能修复,但是既然选择了 Compose,那就以 Compose 的方式来实现吧,见其名知其意,Compose 意思就是组合,Android新的声明式UI,一个用 @Compose 注解标记的函数就代表一个组件,而且可以嵌套组合,越是底层的组合元素越灵活,但是相对应地,需要掌握的东西也比较多,使用难度较高,官方提供了丰富的 Material 组件,对于一般开发者还是比较容易上手的,下面就来使用 DropdownMenu 和 TextButton 组合实现跟 Spinner 一样的组件:

@Composable
fun <T> JSpinner(
    items: List<T>,
    selectedIndex: Int,
    onMenuItemClick: (index: Int, item: T) -> Unit,
    modifier: Modifier = Modifier,
) {
    //使用 AndroidView 加载 android.widget.Spinner 有时候下拉列表无法滚动,数据也容易因为选中事件出错
    var expanded by remember { mutableStateOf(false) }
    var selectedItem by remember { mutableStateOf<String>("") }
    // remember 记忆的委托变量并不是传统意义上的函数内部的局部变量
    // 传给 mutableStateOf() 函数的值只在首次创建变量时有效
    // 所以这里显式地给 selectedItem 赋值,以更新其值
    selectedItem = if (items.isEmpty()) "" else items[selectedIndex].toString()

    var xOffset by remember { mutableStateOf(0) }
    val density = LocalDensity.current.density

    //下面使用 DropdownMenu 和 TextButton 组合实现跟 Spinner 一样的组件
    DropdownMenu(
        offset = DpOffset((xOffset / density + 0.5).dp, 0.dp),
        expanded = expanded,
        onDismissRequest = { expanded = false },
    ) {
        items.forEachIndexed { index, item ->
            DropdownMenuItem(onClick = {
                expanded = false//关闭下拉菜单窗口
                selectedItem = item.toString()
                onMenuItemClick(index, item)
            }) {
                Text(
                    text = item.toString(),
                    color = if (index == selectedIndex) Color(0xFF6200EE) else Color.Black,
                    fontWeight = if (index == selectedIndex) FontWeight.Bold else FontWeight.Normal,
                )
            }
        }
    }
    OutlinedButton(
        modifier = modifier.onGloballyPositioned {
            xOffset = it.positionInWindow().x.toInt()
        },
        onClick = { if (items.isNotEmpty()) expanded = !expanded }
    ) {
        Text(text = selectedItem, modifier = Modifier.weight(1f))
        Icon(if (expanded) Icons.Default.ArrowDropUp else Icons.Default.ArrowDropDown, contentDescription = "")
    }
}


@Preview(showBackground = false)
@Composable
fun JSpinnerPreview() {
    JSpinner(
        items = (0..10).map { "item ${it + 1}" }.toList(),
        selectedIndex = 0,
        onMenuItemClick = { index, item ->
            println("index=$index, item=$item")
        },
    )
}

下面是预览效果:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值