AutocompleteTextView选中选项后仍弹出建议的问题求助
问题解决:AutocompleteTextView选中选项后下拉弹窗反复弹出
问题场景
实现了AutocompleteTextView,设置输入至少3个字符后调用API获取建议列表,但选中选项后,建议弹窗仍反复弹出,尝试clearFocus()和dismissDropDown()方法无效。
核心问题分析
- API响应重复初始化组件:每次接口返回数据都重新创建Adapter、设置点击监听并调用
showDropDown(),强制触发下拉弹窗,覆盖了选中后的关闭操作。 - TextWatcher循环触发:选中选项后调用
setText()会触发TextWatcher的afterTextChanged(),再次发起API请求,形成"选中→设置文本→调API→显示下拉"的循环。 - 阈值设置冲突:接口响应中设置
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
相关产品推荐
相关产品推荐

