由于之前的开发习惯,使用 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")
},
)
}
下面是预览效果: