由于最近在写一个compose项目,需要用到省市区三级联动选择器。之前在使用xml写Android的时候,直接拿别人封装好的开源框架十分方便,又不想用别的麻烦操作,全网也找不到一个现有的、合适的框架,于是心血来潮,连夜搓了一个出来。
主要技术难点:
处理数据。级联选择器必不可少的是数据,数据以json文件形式放在assets中,定义函数拿取json数据并解析成指定的"Province
"对象
fun loadJsonFromAssets(context: Context, fileName: String): String? {
return try {
context.assets.open(fileName).bufferedReader().use { it.readText() }
} catch (ex: IOException) {
ex.printStackTrace()
null
}
}
fun parseProvinces(jsonString: String): List<Province> {
return Json.decodeFromString(jsonString)
}
设计滚动列表和控制滚动状态。选择器由BottomSheet三个小的滚轮组成,使用kotlin的协程和StateManagement监听用户交互和处理滚动动画,确定当前选择项和让选择项始终维持在中部位置。
LaunchedEffect(listState) {
snapshotFlow { listState.layoutInfo.visibleItemsInfo }
.map { visibleItems ->
val viewportStartOffset = listState.layoutInfo.viewportStartOffset
val viewportEndOffset = listState.layoutInfo.viewportEndOffset
val viewportCenter = (viewportEndOffset - viewportStartOffset) / 2 + viewportStartOffset
visibleItems.minByOrNull {
Math.abs((it.offset + it.size / 2) - viewportCenter)
}
}
.collect { centerItem ->
centerItem?.let {
centerIndex.value = it.index
onItemSelected(it.index)
}
}
}
LaunchedEffect(listState) {
listState.interactionSource.interactions.collect { interaction ->
when (interaction) {
is DragInteraction.Stop, is DragInteraction.Cancel -> {
scope.launch {
val visibleItems = listState.layoutInfo.visibleItemsInfo
val viewportStartOffset = listState.layoutInfo.viewportStartOffset
val viewportEndOffset = listState.layoutInfo.viewportEndOffset
val viewportCenter = (viewportEndOffset - viewportStartOffset) / 2 + viewportStartOffset
val centerItem = visibleItems.minByOrNull {
Math.abs((it.offset + it.size / 2) - viewportCenter)
}
centerItem?.let {
listState.animateScrollToItem(it.index)
centerIndex.value = it.index
onItemSelected(it.index)
}
}
}
}
}
}
使用方法:
//setting.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
//build.gradle(app模块)
implementation 'com.github.simonniex:SimonCityPicker:2.2.0'
//Activity/screen/fragment
val context = LocalContext.current
var selectedCity by remember { mutableStateOf("请选择城市") }
var isCity by remember { mutableStateOf(false) }
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth().padding(100.dp)
) {
TextButton(modifier = Modifier.background(Color.Gray),onClick = {
isCity = true
}){
Text(text = selectedCity, color = Color.White)
}
}
SimonCityPicker(
context = context,
isCity = isCity,
onCitySelected = { city ->
selectedCity=city
isCity = false
}
)
效果演示:
总结:
项目地址:
simonniex/SimonCityPicker: 基于JetpackCompose的省市区级联选择器 (github.com)
本开源库是初代(v2.2.0)版本,如果反响不错,我会持续优化,迭代更新。如果喜欢这个项目可以点赞关注一波,欢迎在评论区交流!
以下是分界线
---------------------------------------------------
注意:
如果你自动注入依赖失败,请手动下载并导入