使用compose和WheelView实现仿IOS中的3D滚轮控件-三级联动
文章目录
- 1、资源准备:
- 2、实现效果
- 3、导入wheelview模块
- 3.1 问题与解决
- 3.1.1 问题1-插件找不到
- 解决:
- 3.1.2 问题2-未定义内存空间namespace
- 3.2 添加依赖
- 4、解析json文件
- 4.1 修改json文件
- 4.2 转换json文件中数据
- 5、使用compose实现省市区三级联动
- 5.1 展示WheelView控件
- 5.2 compose实现省市区三级联动
- 5.3 处理json数据并进行展示
1、资源准备:
① 省-市-区/县 三级资源下载
② wheelView依赖模板下载
在资源②中,下载后,将其中的wheelview模块通过Import Module from Source导入自己的项目中。
2、实现效果
3、导入wheelview模块
3.1 问题与解决
3.1.1 问题1-插件找不到
问题:
在导入wheelview后,会出现找不到com.github.dcendents.android-maven的问题
解决:
在wheelview模块的build.gradle中将
apply plugin: ‘com.github.dcendents.android-maven’
修改为
apply plugin: 'maven-publish'
3.1.2 问题2-未定义内存空间namespace
在build.gradle的android {}中添加代码
namespace = "com.contrarywind.view"
3.2 添加依赖
在app的buid.gradle中添加
implementation(project(":wheelview"))
4、解析json文件
4.1 修改json文件
将下载的json资源文件存放在assets文件夹中,并修改json文件为DistrictCityJson.json,结构为
{"data_version": "1.0.0","districts":
{
{"北京市":{"市辖区":["东城区" //为下载的资源文件,也可以不修改,那就需要将下方数据转换的数据结构修改一下
}
的形式。
4.2 转换json文件中数据
创建需要的数据结构,其中RegionData表示的是获取json的数据结构,可通过Gson().fromJson方法将json文件转为实体数据。
RegionResponse为后续方便使用的数据结构,层次结构,方便wheelView使用
data class RegionData(val data_version: String,val districts: Map<String, Map<String, List<String>>>
)
data class RegionResponse(val data_version: String,val districts: List<District>
)data class District(val name: String,val level: Int, // 1:省/直辖市 2:市 3:区县val districts: List<District> = emptyList()
)object RegionDataParser {// 方法1:直接解析为Map结构fun parseRegionDataFromAssets(context: Context, fileName: String): RegionData {return try {val jsonString = context.assets.open(fileName).bufferedReader().use { it.readText() }Gson().fromJson(jsonString, RegionData::class.java)} catch (e: IOException) {throw RuntimeException("Could not read $fileName from assets", e)}}//方法2:将原始数据转为层级的结构fun parseStructuredRegionsFromAssets(context: Context, fileName: String): RegionResponse {val originalList : RegionData = parseRegionDataFromAssets(context,fileName)return RegionResponse(data_version = originalList.data_version,districts = originalList.districts.map { (provinceName, citiesMap) ->District(name = provinceName,level = 1,districts = citiesMap.map { (cityName, districtsList) ->District(name = cityName,level = 2,districts = districtsList.map { districtName ->District(name = districtName,level = 3)})})})}
}
5、使用compose实现省市区三级联动
5.1 展示WheelView控件
WheelView控件在AndroidView中进行初始化和更新的操作,在初始化之前创建其适配器,该适配器在列表变化时重新加载,在
@Composable
fun GetWheelView(modifier: Modifier = Modifier,wheelViewList:List<String> = listOf("北京市","天津市"),onSelected: (String) -> Unit = {}
){val selectedPosition = remember { mutableStateOf(0) }val wheelAdapter = remember(wheelViewList) {object : WheelAdapter<String>{override fun getItemsCount(): Int {return wheelViewList.size}override fun getItem(index: Int): String {return wheelViewList[index]}override fun indexOf(o: String?): Int {return wheelViewList.indexOf(o)}}}AndroidView(modifier = modifier.padding(dimensionResource(R.dimen.padding_top_5)).width(dimensionResource(R.dimen.box_width_100)),factory = {contex->WheelView(contex).apply {setCyclic(false)setDividerType(WheelView.DividerType.WRAP)setCurrentItem(0)setItemsVisibleCount(10)setTextColorCenter(0xFFB11C1E.toInt())setTextSize(18f)setAlphaGradient(false)adapter = wheelAdapter}},update = { wheelView->wheelView.adapter = wheelAdapter// 添加选中监听器以更新状态wheelView.setOnItemSelectedListener { position ->selectedPosition.value = positionLog.d(TAG, "GetWheelView: ${wheelViewList[position]}")onSelected.invoke(wheelViewList[position])}})
}
5.2 compose实现省市区三级联动
@Preview
@Composable
fun AddressPickerScreen(onDismiss: () -> Unit = {},districtsList: List<District> = listOf(District(name = "北京市",level = 1,districts = listOf(District(name = "市辖区",level = 2,districts = listOf(District(name = "丰台区", level = 3))))),District(name = "北京市",level = 1,districts = listOf(District(name = "市辖区",level = 2,districts = listOf(District(name = "西城区", level = 3))))),),onConfirm: (String, String, String) -> Unit = { _, _, _ ->}
) {// 状态管理var selectedProvince by remember { mutableStateOf<District?>(null) }var selectedCity by remember { mutableStateOf<District?>(null) }var selectedDistrict by remember { mutableStateOf<District?>(null) }// 数据列表val provinces = remember { mutableStateListOf<District>() }val cities = remember { mutableStateListOf<District>() }val districts = remember { mutableStateListOf<District>() }// 初始化加载省份数据LaunchedEffect(Unit) {provinces.clear()provinces.addAll(districtsList)selectedProvince = provinces[0]}// 当省份变化时加载城市LaunchedEffect(selectedProvince) {selectedProvince?.let { province ->cities.clear()cities.addAll(districtsList.find { it.name == province.name }?.districts?: listOf())selectedCity = cities[0]selectedDistrict = cities[0].districts[0]} ?: run {cities.clear()districts.clear()}}// 当城市变化时加载区县LaunchedEffect(selectedCity) {selectedCity?.let { city ->districts.clear()districts.addAll(cities.find { it.name == city.name }?.districts?: listOf())selectedDistrict = districts[0]} ?: run {districts.clear()}}// 对话框UIDialog(onDismissRequest = onDismiss) {Column(modifier = Modifier.fillMaxWidth().background(Color.White)) {Row(modifier = Modifier.fillMaxWidth()) {TextButton(modifier = Modifier.weight(5f),onClick = {onDismiss()},enabled = true) {Text("取消", color = Color.Blue)}TextButton(modifier = Modifier.weight(5f),onClick = {onConfirm(selectedProvince?.name ?: "",selectedCity?.name ?: "",selectedDistrict?.name ?: "")},enabled = selectedDistrict != null) {Text("确定", color = Color.Blue)}}// 三级联动选择区域Row(modifier = Modifier.fillMaxWidth()) {// 省份列表GetWheelView(wheelViewList = provinces.map { it.name },onSelected = {selectedProvince = selectedProvince?.copy(name = it)})// 城市列表(当有省份选中时显示)if (selectedProvince != null) {GetWheelView(wheelViewList = cities.map { it.name },onSelected = {selectedCity = selectedCity?.copy(name = it)})}// 区县列表(当有城市选中时显示)if (selectedCity != null) {GetWheelView(wheelViewList = districts.map { it.name },onSelected = {selectedDistrict = selectedDistrict?.copy(name = it)})}}}}
}
5.3 处理json数据并进行展示
@Preview
@Composable
fun PickWheelView(){val context = LocalContext.currentval districtsList = RegionDataParser.parseStructuredRegionsFromAssets(context,"DistrictCityJson.json")AddressPickerScreen(districtsList = districtsList.districts,onConfirm = {province,city,district->Log.d(TAG, "PickWheelView:$province $city $district ")})
}