嵌套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
相关产品推荐
相关产品推荐

