Android Kotlin实现公共键JSON合并映射、RecyclerView展示及搜索
最优实现方案
核心思路是提前在数据层完成两个数据源的关联合并,输出RecyclerView直接可用的统一UI模型,彻底避免bind阶段临时查Map的冗余逻辑,同时天然适配后续搜索需求。
1. 先定义统一的UI数据模型
不要修改接口原有的响应类,单独定义UI层用的合并后实体,做到数据层和UI层解耦:
// 餐厅+菜单合并后的卡片模型 data class RestaurantCardUiModel( val id: Int, val name: String, val neighborhood: String, val photograph: String, val cuisineType: String, val categories: List<Category> // 直接挂载对应餐厅的全部分类、菜单项,无数据时返回空列表 ) // 分类、菜单项可以直接复用现有菜单解析的data class,提前收敛可空类型 data class MenuItem( val id: String, val name: String, val description: String, val price: String ) data class Category( val id: String, val name: String, val menuItems: List<MenuItem> )
2. 用Kotlin集合操作符完成数据关联
拿到两个接口的响应后,不需要手动遍历put到HashMap,用标准集合函数就能完成关联:
// 1. 先把菜单列表转成<餐厅ID, 分类列表>的映射表 val menuGroupedByRestaurantId = menuResponse.menu.associateBy( keySelector = { it.restaurantId }, valueTransform = { it.allcategories.filterNotNull() } // 提前过滤空值,UI层不需要重复判空 ) // 2. 遍历餐厅列表,组装成统一的UI模型列表,作为RecyclerView的最终数据源 val restaurantCardList = restaurantResponse.restaurants.map { restaurant -> RestaurantCardUiModel( id = restaurant.id, name = restaurant.name, neighborhood = restaurant.neighborhood, photograph = restaurant.photograph, cuisineType = restaurant.cuisine_type, // 取对应餐厅的菜单,不存在则返回空列表,彻底规避空指针 categories = menuGroupedByRestaurantId[restaurant.id] ?: emptyList() ) }
3. 简化适配器绑定逻辑
因为传给适配器的每一项已经是带全量菜单的完整数据,onBind阶段不需要做任何查找、判空操作:
// 适配器内部只持有List<RestaurantCardUiModel>类型的数据源 override fun onBindViewHolder(holder: RestaurantAdapter.RestViewHolder, position: Int) { val cardData = restList[position] // 绑定餐厅基础信息 holder.binding.tvRestName.text = cardData.name holder.binding.tvCuisine.text = cardData.cuisineType // 直接拼接分类、菜单项渲染,不需要额外调用getCategoryList val menuDisplayText = cardData.categories.joinToString(separator = "\n") { category -> "${category.name}:${category.menuItems.joinToString("、") { it.name }}" } holder.binding.tvMenuItem.text = menuDisplayText } // 直接删除原有getCategoryList方法,不需要中间Map做中转
4. 搜索过滤功能适配
因为数据源是已经合并好的单列表,过滤逻辑不需要跨两个数据源查询,直接对源列表做filter即可:
fun filterByKeyword(keyword: String): List<RestaurantCardUiModel> { if (keyword.isBlank()) return restaurantCardList // 空关键词返回全量数据 val lowerKeyword = keyword.lowercase() return restaurantCardList.filter { card -> // 匹配规则1:餐厅名称包含关键词 val matchRestaurantName = card.name.lowercase().contains(lowerKeyword) // 匹配规则2:任意分类下的任意菜单项名称包含关键词 val matchMenuItem = card.categories.any { category -> category.menuItems.any { item -> item.name.lowercase().contains(lowerKeyword) } } matchRestaurantName || matchMenuItem }.also { filteredList -> // 将过滤后的列表提交给适配器刷新即可 adapter.submitList(filteredList) } }
方案优势
- 没有绑定阶段的动态Map查找,RecyclerView滚动性能更好
- 空安全逻辑全部收敛在数据合并阶段,UI层没有冗余的
?.let、空判断代码 - 数据结构单一,后续加排序、收藏、标签筛选等功能不需要额外维护多数据源关联关系
- 搜索逻辑直接在合并后的单列表上实现,不需要同步维护两个列表的匹配状态
内容的提问来源于stack exchange,提问作者Cassius
相关产品推荐
相关产品推荐

