You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 02:03:51