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

AutocompleteTextView选中选项后仍弹出建议的问题求助

问题解决:AutocompleteTextView选中选项后下拉弹窗反复弹出

问题场景

实现了AutocompleteTextView,设置输入至少3个字符后调用API获取建议列表,但选中选项后,建议弹窗仍反复弹出,尝试clearFocus()和dismissDropDown()方法无效。

核心问题分析

  1. API响应重复初始化组件:每次接口返回数据都重新创建Adapter、设置点击监听并调用showDropDown(),强制触发下拉弹窗,覆盖了选中后的关闭操作。
  2. TextWatcher循环触发:选中选项后调用setText()会触发TextWatcher的afterTextChanged(),再次发起API请求,形成"选中→设置文本→调API→显示下拉"的循环。
  3. 阈值设置冲突:接口响应中设置threshold=1,和原本要求的"至少3字符触发建议"逻辑矛盾,且每次响应都会重置阈值。

解决方案

1. 优化TextWatcher:限制触发条件+防抖

只在输入长度≥3时发起API请求,同时加入防抖避免输入过程中频繁调用接口:

// 定义防抖Handler
private val searchHandler = Handler(Looper.getMainLooper())
private var searchRunnable: Runnable? = null
// 保存TextWatcher引用,后续用于临时移除
private val textWatcher = object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

    override fun afterTextChanged(s: Editable?) {
        Log.d(Constants.DoctorTag, "inside specialities funtion")
        // 移除之前的延迟任务,避免重复请求
        searchRunnable?.let { searchHandler.removeCallbacks(it) }
        
        if (!s.isNullOrBlank() && s.length >= 3) {
            val specialities = s.toString()
            searchRunnable = Runnable {
                try {
                    Log.d(Constants.DoctorTag, "value send--$specialities")
                    specializationViewModel.getSpecialities(specialities)
                } catch (e: Exception) {
                    Log.e(Constants.DoctorTag, "Invalid input: $e")
                }
            }
            // 延迟300ms执行,防抖
            searchHandler.postDelayed(searchRunnable!!, 300)
        } else {
            // 输入长度不足3时,清空下拉列表
            specialitiesAdapter?.clear()
        }
    }
}

// 绑定TextWatcher
binding.etSpecialization.addTextChangedListener(textWatcher)

2. 初始化时一次性设置Adapter和监听,API响应仅更新数据

不在接口回调中重复创建Adapter和设置点击监听,仅更新Adapter的数据源:

// 初始化阶段(比如onCreate中)创建Adapter并设置
specialitiesAdapter = SpecialitiesAdapter(this, mutableListOf())
binding.etSpecialization.setAdapter(specialitiesAdapter)
binding.etSpecialization.threshold = 3 // 初始设置阈值为3,符合需求

// 设置一次点击监听即可
binding.etSpecialization.setOnItemClickListener { _, _, position, _ ->
    val selcetedSpecialities = specialitiesAdapter.getItem(position)
    if (selcetedSpecialities != null) {
        val specialities = selcetedSpecialities.title
        // 暂时移除TextWatcher,避免设置文本时触发API请求
        binding.etSpecialization.removeTextChangedListener(textWatcher)
        binding.etSpecialization.setText(specialities)
        specialtyId = selcetedSpecialities.id
        // 恢复TextWatcher
        binding.etSpecialization.addTextChangedListener(textWatcher)
        // 关闭下拉弹窗
        binding.etSpecialization.dismissDropDown()
        binding.etSpecialization.clearFocus()
    } else {
        Toast.makeText(this, "无效选项", Toast.LENGTH_LONG).show()
        binding.etSpecialization.clearFocus()
    }
}

// 接口响应仅更新Adapter数据
specializationViewModel.specialities.observe(this){
    when(it){
        is NetworkResponseHandler.Success ->{
            val specialitiesList = it.data?.data
            Log.d(Constants.DoctorTag, "This is specialities list ------${specialitiesList}")
            try {
                if (specialitiesList != null){
                    Log.d(Constants.DoctorTag, "list is not null")
                    // 仅更新Adapter数据,不重新创建
                    specialitiesAdapter.updateData(specialitiesList)
                } else {
                    Log.d(Constants.DoctorTag, "Specialities list is null------")
                    Toast.makeText(this,"Specialities not found",Toast.LENGTH_LONG).show()
                    specialitiesAdapter.clear()
                }
            }catch (e:Exception){
                Log.d(Constants.DoctorTag, "specialities list exception---$e")
            }
        }
        is NetworkResponseHandler.Error ->{
            Log.d(Constants.DoctorTag, "error: ${it.massage}")
            specialitiesAdapter.clear()
        }
        is NetworkResponseHandler.Loading ->{
            Log.d(Constants.DoctorTag, "loading: ${it.massage}")
        }
    }
}

3. 给Adapter添加数据更新方法

需要在SpecialitiesAdapter中添加更新数据的方法,避免每次重新创建Adapter:

class SpecialitiesAdapter(context: Context, private var dataList: MutableList<YourSpecialityModel>) :
    ArrayAdapter<YourSpecialityModel>(context, android.R.layout.simple_dropdown_item_1line, dataList) {

    // 添加更新数据的方法
    fun updateData(newList: List<YourSpecialityModel>) {
        dataList.clear()
        dataList.addAll(newList)
        notifyDataSetChanged()
    }

    fun clear() {
        dataList.clear()
        notifyDataSetChanged()
    }

    // 其他原有方法...
}

关键说明

  • 防抖机制避免了输入过程中频繁的API请求,提升性能同时减少无效弹窗。
  • 移除TextWatcher后再设置文本,切断了"设置文本→触发API→显示下拉"的循环。
  • 仅更新Adapter数据而非重新创建组件,避免了不必要的组件初始化和下拉触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:23:20