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

Android Studio中RecyclerView的RadioButton返回页面时状态重置方法

RecyclerView中RadioButton重进Fragment选中状态残留修复

问题现象

  • RecyclerView内的RadioButton单次页面内交互逻辑正常
  • 选中选项后返回上一级Fragment,再次进入当前页面时,RadioButton会默认保持上次最后一项的选中状态,未实现状态重置
  • 首次进入预期效果:首次进入无选中的预期效果
  • 重进页面异常效果:重进后残留选中状态的异常效果

原有实现代码

Fragment代码

private fun initializeRecyclerView(){
    val transactionTypeLayoutManager = GridLayoutManager(context, 2)
    
    val list = TransactionType.values().toList()
    val transactionTypeAdapter = ManualMixTransactionTypeAdapter(list)
    binding?.recyclerViewTransactionType?.layoutManager = transactionTypeLayoutManager
    binding?.recyclerViewTransactionType?.adapter = transactionTypeAdapter

    transactionTypeAdapter.onTransactionTypeClicked = {
        viewModel.setTransactionType(it.name)
        Toast.makeText(context, "${it.name}", Toast.LENGTH_SHORT).show()
    }
}

Adapter代码

var onTransactionTypeClicked: ((TransactionType) -> Unit)? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionTypeViewHolder {
    val layoutInflater: LayoutInflater = LayoutInflater.from(parent.context)
    val binding: ViewholderTransactionTypeBinding = ViewholderTransactionTypeBinding.inflate(layoutInflater, parent, false)
    return TransactionTypeViewHolder(binding)
}

override fun onBindViewHolder(holder: TransactionTypeViewHolder, position: Int) {
    holder.binding.rbTransactionType.setText(transactionType[position].key)
    holder.render(
        data = transactionType[position],
        dataIndex = position,
        adapterListener = {
            val oldTransactionType = transactionType.find { transactionType -> transactionType.isItemSelected }

            oldTransactionType?.apply { isItemSelected = false }

            val oldPosition = transactionType.indexOf(oldTransactionType)

            notifyItemChanged(oldPosition)
        },
        updateListener = { newPosition -> notifyItemChanged(newPosition)},
        clickListener = onTransactionTypeClicked
    )
}

ViewHolder代码

fun render(
    data: TransactionType,
    dataIndex: Int,
    adapterListener: (() -> Unit),
    updateListener: ((Int) -> Unit),
    clickListener: ((TransactionType) -> Unit)?
) {
    val onClickListener: View.OnClickListener = View.OnClickListener {

        adapterListener.invoke()

        data.isItemSelected = true

        updateListener.invoke(dataIndex)

        clickListener?.invoke(data)
    }

    binding.vhTransactionType.setOnClickListener(onClickListener)
    binding.rbTransactionType.setOnClickListener(onClickListener)

    binding.rbTransactionType.isChecked = data.isItemSelected
}

问题根因

选中状态isItemSelected直接定义在TransactionType枚举类中,枚举本质是全局单例实例,即使退出Fragment、新建Adapter,枚举实例上之前修改的isItemSelected值会一直保留,下次进入页面读取枚举值时就会拿到上次残留的选中状态。

修复方案

方案1(最小改动)

初始化列表时主动重置所有枚举项的选中状态,清除残留值,修改Fragment中initializeRecyclerView方法即可:

private fun initializeRecyclerView(){
    val transactionTypeLayoutManager = GridLayoutManager(context, 2)
    
    val list = TransactionType.values().toList()
    // 初始化时重置所有选项状态
    list.forEach { it.isItemSelected = false }
    val transactionTypeAdapter = ManualMixTransactionTypeAdapter(list)
    binding?.recyclerViewTransactionType?.layoutManager = transactionTypeLayoutManager
    binding?.recyclerViewTransactionType?.adapter = transactionTypeAdapter

    transactionTypeAdapter.onTransactionTypeClicked = {
        viewModel.setTransactionType(it.name)
        Toast.makeText(context, "${it.name}", Toast.LENGTH_SHORT).show()
    }
}

方案2(更优实践,避免全局状态污染)

不要把选中状态存在全局单例的枚举类中,改为Adapter内部维护选中位置,状态随Adapter生命周期绑定,Adapter销毁时状态自动清空,不会出现跨页面状态污染问题:

class ManualMixTransactionTypeAdapter(
    private val transactionType: List<TransactionType>
) : RecyclerView.Adapter<TransactionTypeViewHolder>() {

    var onTransactionTypeClicked: ((TransactionType) -> Unit)? = null
    // 选中位置默认-1,代表无选中项,状态仅存在于Adapter实例中
    private var selectedPosition = -1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionTypeViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ViewholderTransactionTypeBinding.inflate(layoutInflater, parent, false)
        return TransactionTypeViewHolder(binding)
    }

    override fun onBindViewHolder(holder: TransactionTypeViewHolder, position: Int) {
        val currentItem = transactionType[position]
        holder.binding.rbTransactionType.setText(currentItem.key)
        // 根据当前选中位置设置RadioButton状态
        holder.binding.rbTransactionType.isChecked = position == selectedPosition

        val clickListener = View.OnClickListener {
            val currentPos = holder.bindingAdapterPosition
            if (currentPos == selectedPosition) return@OnClickListener
            // 刷新旧选中项
            val oldPos = selectedPosition
            selectedPosition = currentPos
            if (oldPos != -1) notifyItemChanged(oldPos)
            notifyItemChanged(selectedPosition)
            onTransactionTypeClicked?.invoke(currentItem)
        }

        holder.binding.vhTransactionType.setOnClickListener(clickListener)
        holder.binding.rbTransactionType.setOnClickListener(clickListener)
    }

    override fun getItemCount(): Int = transactionType.size
}

对应ViewHolder可以简化,不需要再传递多层回调处理选中逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 00:21:32