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

实现ListView整体垂直滚动,点击项后子任务横向无压缩右移展开

我刚好折腾过类似的层级任务横向展开的需求,你的问题核心就是要避免子任务展开时把父任务挤变形,同时还要有层级右移的视觉效果。下面是亲测有效的实现方案,一步步来:

解决方案步骤

1. 先搞定递归的任务数据结构

首先得有个支持嵌套层级的数据类,用来存任务、子任务,还要记录当前是否展开:

data class Task(
    val id: String,
    val title: String,
    val children: MutableList<Task> = mutableListOf(),
    var isExpanded: Boolean = false, // 标记当前任务是否展开
    val level: Int = 0 // 记录任务层级,用来做右移缩进
)

这里的level字段特别重要,用来控制每个任务项的左边距,实现层级越深越靠右的效果。

2. 设计任务项的布局(item_task.xml)

要避免压缩,核心是让父任务的内容区域宽度固定,子任务在横向方向展开。这里用ConstraintLayout搭配横向RecyclerView来实现:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingVertical="8dp">

    <!-- 任务标题区域,固定宽度,防止被压缩 -->
    <TextView
        android:id="@+id/tv_task_title"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:paddingHorizontal="16dp"
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <!-- 展开/折叠按钮 -->
    <ImageView
        android:id="@+id/iv_expand_toggle"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginStart="8dp"
        android:src="@drawable/ic_expand_more"
        app:layout_constraintStart_toEndOf="@id/tv_task_title"
        app:layout_constraintTop_toTopOf="@id/tv_task_title" />

    <!-- 子任务横向容器,默认隐藏,展开时显示 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_sub_tasks"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        app:layout_constraintStart_toEndOf="@id/iv_expand_toggle"
        app:layout_constraintTop_toTopOf="@id/tv_task_title"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layoutManagerOrientation="horizontal" />

</androidx.constraintlayout.widget.ConstraintLayout>

这里的关键细节:

  • 任务标题设固定宽度(比如120dp),不会因为子任务展开被挤窄
  • 子任务用横向RecyclerView,展开时在父任务右侧横向排列,层级缩进靠level控制

3. 实现递归的RecyclerView Adapter

因为任务是嵌套层级,Adapter要递归处理子任务,同时搞定展开/折叠逻辑:

class TaskAdapter(private val tasks: List<Task>) : RecyclerView.Adapter<TaskAdapter.TaskViewHolder>() {

    inner class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvTitle: TextView = itemView.findViewById(R.id.tv_task_title)
        val ivToggle: ImageView = itemView.findViewById(R.id.iv_expand_toggle)
        val rvSubTasks: RecyclerView = itemView.findViewById(R.id.rv_sub_tasks)

        fun bind(task: Task) {
            tvTitle.text = task.title
            // 根据层级设置左边距,实现右移缩进
            val indent = task.level * 32 // 每层缩进32dp,可根据需求调整
            val params = tvTitle.layoutParams as MarginLayoutParams
            params.leftMargin = indent
            tvTitle.layoutParams = params

            // 显示/隐藏子任务容器
            rvSubTasks.visibility = if (task.isExpanded) View.VISIBLE else View.GONE
            // 更新展开按钮图标
            ivToggle.setImageResource(if (task.isExpanded) R.drawable.ic_expand_less else R.drawable.ic_expand_more)

            // 给子任务设置Adapter,递归嵌套
            rvSubTasks.adapter = TaskAdapter(task.children)

            // 点击任务切换展开状态
            itemView.setOnClickListener {
                task.isExpanded = !task.isExpanded
                notifyItemChanged(adapterPosition)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_task, parent, false)
        return TaskViewHolder(view)
    }

    override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
        holder.bind(tasks[position])
    }

    override fun getItemCount() = tasks.size
}

核心逻辑说明:

  • 用level计算左边距,实现层级右移的视觉效果
  • 点击时切换isExpanded状态,通知Adapter更新当前item,显示/隐藏子任务横向列表
  • 子任务的RecyclerView复用同一个Adapter,实现递归嵌套展示

4. 主布局和初始化设置

主布局里的RecyclerView要设置横向的LinearLayoutManager,因为整个任务列表是横向展开的:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_main_tasks"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical="16dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layoutManagerOrientation="horizontal"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后在Activity/Fragment里初始化示例数据和Adapter:

// 示例嵌套任务数据
val rootTasks = listOf(
    Task(
        id = "1",
        title = "Task A",
        children = mutableListOf(
            Task(
                id = "1-1",
                title = "Task AA",
                level = 1,
                children = mutableListOf(
                    Task(id = "1-1-1", title = "Task AAA", level = 2)
                )
            )
        )
    ),
    Task(id = "2", title = "Task B", level = 0)
)

rv_main_tasks.adapter = TaskAdapter(rootTasks)

额外优化提示

  • 如果任务层级很深,建议用DiffUtil优化Adapter更新,避免不必要的视图刷新
  • 可以缓存子任务的Adapter实例,减少重复创建带来的性能损耗

这样实现后,点击任务时子任务会在父任务右侧横向展开,每个层级的任务自动右移缩进,而且父任务的内容完全不会被压缩,完美符合你的需求。

内容的提问来源于stack exchange,提问作者Shashi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:24:58