Kotlin中如何让可展开列表头部显示正确的子项数量
解决Kotlin中ExpandableListView分组头部子项数量显示异常的问题
我来帮你搞定这个头疼的分组数量显示问题!结合你描述的场景(API返回今日/明日/未来任务及对应总数,用HashMap存分组数据),核心问题大概率是分组的总数和子项数据没有正确绑定,或者你误用了子项列表的size来显示总数(如果API返回的总数和实际子项数量不一致的话)。下面是一步步的解决方案:
1. 重构分组数据模型,避免HashMap的匹配问题
别再用单纯的HashMap<String, List<Task>>了,创建一个自定义数据类把分组的标题、总数、子项列表绑定在一起,从根源上避免键值对不匹配的问题:
// 定义分组数据类,把标题、总数、子项打包 data class TaskGroup( val groupTitle: String, val totalTaskCount: Int, val taskList: List<Task> )
2. 重写ExpandableListAdapter,直接绑定API返回的总数
自定义Adapter时,在getGroupView方法里直接使用TaskGroup里的totalTaskCount,而不是去计算taskList.size(毕竟API返回的总数可能包含未加载的分页任务,或者和实际返回的子项数量有差异)。示例代码如下:
class TaskExpandableAdapter( private val context: Context, private val groupList: List<TaskGroup> ) : BaseExpandableListAdapter() { // 获取分组总数 override fun getGroupCount(): Int = groupList.size // 获取当前分组的子项数量(用于列表展开/折叠逻辑) override fun getChildrenCount(groupPosition: Int): Int = groupList[groupPosition].taskList.size override fun getGroup(groupPosition: Int): Any = groupList[groupPosition] override fun getChild(groupPosition: Int, childPosition: Int): Any = groupList[groupPosition].taskList[childPosition] override fun getGroupId(groupPosition: Int): Long = groupPosition.toLong() override fun getChildId(groupPosition: Int, childPosition: Int): Long = childPosition.toLong() override fun hasStableIds(): Boolean = true // 渲染分组头部视图 override fun getGroupView( groupPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup? ): View { val headerView = convertView ?: LayoutInflater.from(context) .inflate(R.layout.layout_group_header, parent, false) // 获取当前分组的数据 val currentGroup = groupList[groupPosition] // 设置分组标题和总数 headerView.findViewById<TextView>(R.id.tv_group_title).text = currentGroup.groupTitle // 这里直接用API返回的totalTaskCount,而不是taskList.size! headerView.findViewById<TextView>(R.id.tv_task_count).text = "(${currentGroup.totalTaskCount})" // 可选:根据展开状态调整箭头图标 val ivArrow = headerView.findViewById<ImageView>(R.id.iv_expand_arrow) ivArrow.setImageResource(if (isExpanded) R.drawable.ic_arrow_up else R.drawable.ic_arrow_down) return headerView } // 渲染子项视图 override fun getChildView( groupPosition: Int, childPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup? ): View { val childView = convertView ?: LayoutInflater.from(context) .inflate(R.layout.layout_task_item, parent, false) val currentTask = groupList[groupPosition].taskList[childPosition] childView.findViewById<TextView>(R.id.tv_task_title).text = currentTask.title // 其他子项UI逻辑... return childView } }
3. 组装API数据并绑定Adapter
从API获取ToDoListResponse后,把数据转换成TaskGroup列表,再设置给ExpandableListView:
// 假设你的API接口返回ToDoListResponse,包含todayCount、todayTasks等字段 apiClient.getTodoList().enqueue(object : Callback<ToDoListResponse> { override fun onResponse(call: Call<ToDoListResponse>, response: Response<ToDoListResponse>) { response.body()?.let { todoResp -> val taskGroups = mutableListOf<TaskGroup>().apply { add(TaskGroup("今日", todoResp.todayCount, todoResp.todayTasks)) add(TaskGroup("明日", todoResp.tomorrowCount, todoResp.tomorrowTasks)) add(TaskGroup("未来", todoResp.futureCount, todoResp.futureTasks)) } // 初始化Adapter并绑定到ExpandableListView val adapter = TaskExpandableAdapter(requireContext(), taskGroups) expandableListView.setAdapter(adapter) // 可选:默认展开所有分组 for (i in 0 until adapter.groupCount) { expandableListView.expandGroup(i) } } } override fun onFailure(call: Call<ToDoListResponse>, t: Throwable) { // 处理请求失败逻辑,比如显示Toast Toast.makeText(context, "加载任务失败:${t.message}", Toast.LENGTH_SHORT).show() } })
4. 常见排查点
如果还是有问题,检查这几个地方:
- API数据准确性:确认返回的
todayCount、tomorrowCount等字段值是否正确,有没有和实际子项数量不符的情况。 - 视图复用问题:在
getGroupView里一定要确保每次都重新绑定数据,不要因为convertView复用导致旧数据残留。 - 分组匹配问题:之前用HashMap时,可能存在分组标题字符串不匹配的情况(比如大小写、空格),用
TaskGroup列表完全避免了这个问题。
内容的提问来源于stack exchange,提问作者Jay Mistry
相关产品推荐
相关产品推荐

