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

调用notifyDataSetChanged后EditText失去焦点的解决方案求助

问题根因

你现有代码存在两个核心问题导致焦点恢复失败:

  1. 每次更新列表时直接清空所有数据、创建全新的TestDataObject实例,之前保存在旧对象里的焦点状态完全丢失,自然无法恢复焦点。
  2. 全量调用notifyDataSetChanged会触发所有item重绘,系统会主动清除所有子View的焦点,导致你设置的焦点监听器只能拿到false的状态值。

最优解决方案:使用DiffUtil做局部刷新

该方案不需要额外处理焦点逻辑,只要不刷新持有焦点的item,焦点就不会丢失,同时大幅提升列表刷新性能。

步骤1:改造数据类和比对规则

给TestDataObject新增唯一标识id,用于DiffUtil比对item:

data class TestDataObject(
    val id: Int, // 每个item的id固定不变
    val titleText: String = "", 
    val infoText: String = "", 
    var fieldHasFocus: Boolean = false
)

新增DiffUtil比对规则:

class TestDataDiffCallback : DiffUtil.ItemCallback<TestDataObject>() {
    override fun areItemsTheSame(oldItem: TestDataObject, newItem: TestDataObject): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: TestDataObject, newItem: TestDataObject): Boolean {
        // 仅内容变化时才刷新item,焦点状态变更不触发刷新
        return oldItem.titleText == newItem.titleText 
                && oldItem.infoText == newItem.infoText
    }
}

步骤2:改造适配器继承ListAdapter

ListAdapter会自动根据DiffUtil比对结果做局部刷新,不需要手动调用notify系列方法:

class ViewPagerAdapter : ListAdapter<TestDataObject, ViewPagerAdapter.TestHolder>(TestDataDiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.holder_test, parent, false)
        val binding: HolderTestBinding = DataBindingUtil.bind(view)!!
        return TestHolder(binding)
    }

    override fun onBindViewHolder(holder: TestHolder, position: Int) {
        holder.bind(getItem(position))
    }

    inner class TestHolder(private val binding: HolderTestBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(testDataObject: TestDataObject) {
            binding.apply {
                titleTv.text = testDataObject.titleText
                textTv.text = testDataObject.infoText
                editText.setOnFocusChangeListener { _, focus ->
                    testDataObject.fieldHasFocus = focus
                }
            }
        }
    }
}

步骤3:改造数据更新逻辑

更新列表时要迁移旧列表的焦点状态到新对象上,避免状态丢失:

private fun setDataToList() {
    val oldList = testList.toList()
    testList.clear()
    repeat(6) { id ->
        // 迁移旧的焦点状态
        val oldFocusState = oldList.find { it.id == id }?.fieldHasFocus ?: false
        testList.add(
            TestDataObject(
                id = id,
                titleText = "title for page $id ${Random.nextInt()}",
                infoText = "infoText for page $id ${Random.nextInt()}",
                fieldHasFocus = oldFocusState
            )
        )
    }
}
// 数据更新时调用submitList触发局部刷新
_testLiveData.observeEvent(this) {
    adapter?.submitList(it)
    constantlyUpdateList()
}

兼容方案:必须全量刷新时的处理逻辑

如果业务场景必须使用notifyDataSetChanged全量刷新,可以通过主动记录焦点位置、布局完成后再恢复焦点的方式实现:

改造适配器代码

class ViewPagerAdapter(var list: List<TestDataObject>) : RecyclerView.Adapter<ViewPagerAdapter.TestHolder>() {
    // 记录持有焦点的item对应id
    private var focusItemId: Int = -1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.holder_test, parent, false)
        val binding: HolderTestBinding = DataBindingUtil.bind(view)!!
        return TestHolder(binding)
    }

    override fun onBindViewHolder(holder: TestHolder, position: Int) {
        val item = list[position]
        holder.bind(item, item.id == focusItemId)
    }

    override fun getItemCount(): Int = list.size

    fun updateData(items: List<TestDataObject>) {
        // 更新数据前先同步焦点状态
        val currentFocusItem = list.find { it.id == focusItemId }
        focusItemId = items.find { it.id == currentFocusItem?.id }?.id ?: -1
        list = items
        notifyDataSetChanged()
    }

    inner class TestHolder(private val binding: HolderTestBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(testDataObject: TestDataObject, needFocus: Boolean) {
            binding.apply {
                titleTv.text = testDataObject.titleText
                textTv.text = testDataObject.infoText
                // 实时记录持有焦点的item id
                editText.setOnFocusChangeListener { _, focus ->
                    if (focus) focusItemId = testDataObject.id
                }
                if (needFocus) {
                    // 等待布局绘制完成后再申请焦点,比固定延迟可靠
                    editText.post {
                        editText.requestFocus()
                        // 可选:自动弹出软键盘
                        val imm = editText.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                        imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT)
                    }
                }
            }
        }
    }

}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 15:57:01