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

Android Studio 自定义Dialog内RecyclerView中EditText失焦问题

问题:Dialog嵌套RecyclerView中EditText输入单字符后自动失焦
  • 开发场景:Android Studio开发环境下,自定义Dialog中嵌套RecyclerView,RecyclerView条目内包含EditText控件
  • 异常表现:点击EditText后仅能输入1个字符,控件就会自动丢失输入焦点
  • 排查背景:普通RecyclerView直接嵌套EditText的同类解决方案在当前场景下全部无效,问题触发核心是双层嵌套结构(RecyclerView位于自定义Dialog内部)

附1:自定义Dialog布局代码(包含嵌套的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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foregroundTint="#009688">

    <androidx.cardview.widget.CardView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <EditText
                android:id="@+id/nombreGasto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:hint="Nombre"
                android:inputType="textPersonName"
                android:minHeight="48dp" />

            <EditText
                android:id="@+id/precioGasto"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:hint="Precio"
                android:inputType="numberDecimal"
                android:minHeight="48dp" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerGasto"
                android:layout_width="match_parent"
                android:layout_height="145dp" />

            <Space
                android:layout_width="match_parent"
                android:layout_height="10dp" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <Button
                    android:id="@+id/cancelButton"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:backgroundTint="#FFFFFF"
                    android:text="Cancel"
                    android:textColor="#00BCD4" />

                <Button
                    android:id="@+id/addButton"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:backgroundTint="#FFFFFF"
                    android:text="OK"
                    android:textColor="#00BCD4" />
            </LinearLayout>
        </LinearLayout>

    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

附2:RecyclerView条目布局代码(包含触发异常的EditText)

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:id="@+id/linearLay"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/personNamePrecio"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:text="PersonaX"
                android:textAlignment="viewStart"
                android:textSize="24sp"
                android:textStyle="bold" />

            <EditText
                android:id="@+id/editTextPrecioPersona"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:ems="10"
                android:hint="0.00"
                android:inputType="numberDecimal"
                android:minHeight="48dp" />

        </LinearLayout>
    </androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

解决方案

问题核心原因:EditText输入内容后触发RecyclerView全局重绘,Dialog窗口焦点处理逻辑和RecyclerView焦点复用逻辑冲突,导致每次文本变更后控件被重新布局、焦点被回收。按以下步骤修改即可修复:

  1. 调整RecyclerView基础配置
    在Dialog初始化代码中找到RecyclerView实例,添加以下配置,禁用布局时的焦点自动回收:
    recyclerGasto.apply {
        // 禁用子视图变化触发的全局焦点重新分配
        isFocusableInTouchMode = false
        // 子控件优先获取焦点,父布局不抢占
        descendantFocusability = ViewGroup.FOCUS_AFTER_DESCENDANTS
        // 固定RecyclerView高度,避免输入时高度变化触发全量重绘
        setHasFixedSize(true)
    }
    
    Java代码对应写法:
    RecyclerView recyclerGasto = findViewById(R.id.recyclerGasto);
    recyclerGasto.setFocusableInTouchMode(false);
    recyclerGasto.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    recyclerGasto.setHasFixedSize(true);
    
  2. 修正适配器文本监听逻辑
    禁止在onBindViewHolder中给EditText设置「文本变化就直接更新数据源、调用notifyDataSetChanged()」的逻辑,这会触发整个列表重绘,直接夺走输入焦点。正确逻辑:
    • 给EditText绑定OnFocusChangeListener,只有控件失去焦点时,才把输入内容同步到对应位置的数据源
    • 如果需要实时同步,用notifyItemChanged(adapterPosition, payload)做局部更新,不要调用全量刷新的notifyDataSetChanged()
      参考写法:
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val itemData = dataList[position]
        // 先移除旧的文本监听,避免视图复用触发逻辑错乱
        holder.editText.removeTextChangedListener(holder.textWatcher)
        // 填充已保存文本,不要每次绑定都重置光标位置
        if (holder.editText.text.toString() != itemData.precio) {
            holder.editText.setText(itemData.precio)
        }
        // 失焦时再同步数据到数据源
        holder.editText.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus ->
            if (!hasFocus) {
                val input = (v as EditText).text.toString()
                dataList[position].precio = input
            }
        }
    }
    
  3. 补充Dialog窗口配置
    在自定义Dialog的onCreate方法中添加窗口软键盘配置,避免弹窗默认焦点拦截逻辑干扰输入:
    window?.apply {
        clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
        clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
        setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
    }
    
  4. 补全条目布局焦点属性
    给条目布局里的editTextPrecioPersona添加两个属性,避免布局重排时焦点丢失:
    <EditText
        android:id="@+id/editTextPrecioPersona"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ems="10"
        android:hint="0.00"
        android:inputType="numberDecimal"
        android:minHeight="48dp"
        android:focusable="true"
        android:focusableInTouchMode="true"
        />
    

注意:如果适配器里给EditText设置了setOnClickListener,不要在点击事件里调用任何刷新列表的方法,否则点击时就会触发重绘夺走焦点。

内容的提问来源于stack exchange,提问作者Adrián Mon Maroto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:57:24