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

嵌套RecyclerView全局唯一选中项高亮实现求助

解决嵌套RecyclerView全局唯一选中项问题

核心思路

每个子Adapter单独维护选中状态会导致状态隔离,因此需要全局统一管理选中状态,通过LiveData让所有子Adapter监听状态变化,一旦选中项更新,所有子RecyclerView同步更新高亮状态。

步骤1:创建全局状态管理的ViewModel

用ViewModel保存全局选中状态,确保页面生命周期内状态不丢失,同时让所有子Adapter能观察到状态变化:

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel

class SelectedItemViewModel : ViewModel() {
    // 用Pair保存选中的父项索引+子项索引,初始(-1,-1)表示未选中
    private val _selectedPosition = MutableLiveData<Pair<Int, Int>>(-1 to -1)
    val selectedPosition: LiveData<Pair<Int, Int>> = _selectedPosition

    // 更新选中位置
    fun updateSelectedPosition(parentPos: Int, childPos: Int) {
        _selectedPosition.value = parentPos to childPos
    }
}

步骤2:修改ParentAdapter,传递ViewModel给子Adapter

ParentAdapter持有ViewModel实例,创建ChildAdapter时传入当前父项索引和ViewModel,让子Adapter知道自己所属的父项:

class ParentAdapter(
    private val parentData: List<ParentItem>,
    private val selectedViewModel: SelectedItemViewModel
) : RecyclerView.Adapter<ParentAdapter.ParentViewHolder>() {

    override fun onBindViewHolder(viewHolder: ParentViewHolder, position: Int) {
        val parentItem = parentData[position]
        viewHolder.ParentTitle.text = parentItem.ParentName

        val childAdapter = ChildAdapter(
            childList = parentItem.childList,
            parentIndex = position,
            selectedViewModel = selectedViewModel
        )
        viewHolder.childRecycler.layoutManager = LinearLayoutManager(
            viewHolder.childRecycler.context,
            LinearLayoutManager.VERTICAL,
            false
        )
        viewHolder.childRecycler.adapter = childAdapter
    }

    // onCreateViewHolder、getItemCount等其他代码保持不变
}

步骤3:重构ChildAdapter,依赖全局状态

去掉ChildAdapter自身的selectedItem,改为观察ViewModel的全局选中状态,判断当前项是否为选中项:

class ChildAdapter(
    private val childList: MutableList<ChildItem>,
    private val parentIndex: Int,
    private val selectedViewModel: SelectedItemViewModel
) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() {

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

    override fun onBindViewHolder(viewHolder: ChildViewHolder, position: Int) {
        val childItem = childList[position]
        // 根据全局状态判断是否选中,设置背景色
        val isSelected = selectedViewModel.selectedPosition.value == (parentIndex to position)
        viewHolder.itemView.setBackgroundColor(if (isSelected) Color.GREY else Color.WHITE)
    }

    inner class ChildViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        init {
            view.setOnClickListener {
                val childPos = adapterPosition
                if (childPos == RecyclerView.NO_POSITION) return@setOnClickListener
                // 更新全局选中状态
                selectedViewModel.updateSelectedPosition(parentIndex, childPos)
            }
        }
    }

    override fun getItemCount() = childList.size
}

步骤4:在宿主页面初始化ViewModel并传递给ParentAdapter

在Activity/Fragment中获取ViewModel实例,传给ParentAdapter,确保所有子Adapter共享同一个状态源:

// 以Activity为例
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val selectedViewModel = ViewModelProvider(this)[SelectedItemViewModel::class.java]
        val parentAdapter = ParentAdapter(parentData = yourParentDataList, selectedViewModel = selectedViewModel)
        parentRecyclerView.adapter = parentAdapter
    }
}

关键说明

  • 用Pair<Int, Int>保存选中位置,避免不同父项的子项索引冲突;
  • LiveData自动通知所有观察者,状态变化时所有子RecyclerView同步更新高亮;
  • 简化了原有的payloads逻辑,直接通过全局状态判断并更新UI,逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 15:54:52