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

Android AppCompatAutoCompleteTextView无匹配默认项及词高亮问题

AppCompatAutoCompleteTextView 联想功能优化实现

直接覆盖两个核心需求:无匹配结果兜底提示、匹配关键词加粗。

核心实现思路

原生ArrayAdapter的过滤逻辑和视图渲染不支持自定义扩展,直接继承ArrayAdapter重写过滤逻辑和item渲染逻辑即可,不需要额外引入第三方控件。

  • 过滤逻辑:执行常规关键词匹配后,如果匹配列表为空,自动把预设的Other兜底项加入返回结果
  • 视图渲染:渲染每个下拉项时,提取当前输入的关键词,把文本中对应位置的片段设置为粗体样式

具体代码实现

1. 自定义联想适配器

import android.content.Context
import android.graphics.Typeface
import android.text.Spannable
import android.text.SpannableString
import android.text.style.StyleSpan
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import android.widget.TextView

class TopicSuggestAdapter(
    context: Context,
    itemLayoutRes: Int,
    private val allSuggestItems: Array<String>
) : ArrayAdapter<String>(context, itemLayoutRes, allSuggestItems.toMutableList()) {

    private val currentDisplayList = mutableListOf<String>()
    private var currentInput = ""

    override fun getCount(): Int = currentDisplayList.size
    override fun getItem(position: Int): String = currentDisplayList[position]

    override fun getFilter(): Filter = object : Filter() {
        override fun performFiltering(input: CharSequence?): FilterResults {
            currentInput = input?.toString()?.trim() ?: ""
            val result = FilterResults()
            val matchItems = mutableListOf<String>()

            when {
                currentInput.isEmpty() -> {
                    // 输入为空时展示全部联想项
                    matchItems.addAll(allSuggestItems)
                }
                else -> {
                    // 常规匹配:遍历词库匹配包含输入关键词的项,忽略大小写
                    allSuggestItems.forEach { item ->
                        if (item != "Other" && item.contains(currentInput, true)) {
                            matchItems.add(item)
                        }
                    }
                    // 无匹配结果时加入兜底项Other
                    if (matchItems.isEmpty()) {
                        matchItems.add("Other")
                    }
                }
            }

            result.values = matchItems
            result.count = matchItems.size
            return result
        }

        override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
            currentDisplayList.clear()
            results?.values?.let {
                currentDisplayList.addAll(it as List<String>)
            }
            if (results?.count ?: 0 > 0) notifyDataSetChanged() else notifyDataSetInvalidated()
        }
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        val itemView = super.getView(position, convertView, parent)
        val tvContent = itemView.findViewById<TextView>(android.R.id.text1)
        val itemText = currentDisplayList[position]

        // 非兜底项才做关键词加粗处理
        if (currentInput.isNotEmpty() && itemText != "Other") {
            val keywordStart = itemText.indexOf(currentInput, ignoreCase = true)
            if (keywordStart >= 0) {
                val spannable = SpannableString(itemText)
                spannable.setSpan(
                    StyleSpan(Typeface.BOLD),
                    keywordStart,
                    keywordStart + currentInput.length,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                )
                tvContent.text = spannable
                return itemView
            }
        }
        tvContent.text = itemText
        return itemView
    }
}

2. 替换原有原生适配器

把原来初始化ArrayAdapter的逻辑替换为自定义适配器即可:

binding.topicEt.apply {
    threshold = 1 // 输入1个字符即触发联想,可按需调整为0或其他值
    val topicArray = resources.getStringArray(R.array.feedback_topic_array)
    adapter = TopicSuggestAdapter(
        context,
        android.R.layout.simple_dropdown_item_1line,
        topicArray
    )
    // 需要页面加载后默认展开下拉则保留,否则删除
    post { showDropDown() }
}

可选调整点

  • 匹配规则修改:如果需要前缀匹配而不是全局包含匹配,把过滤逻辑里的item.contains(currentInput, true)替换为item.startsWith(currentInput, true)即可
  • 样式扩展:如果需要给匹配关键词加颜色、字号调整,把StyleSpan(Typeface.BOLD)替换为ForegroundColorSpan、AbsoluteSizeSpan等对应Span即可
  • 避免硬编码:可以把兜底项Other提取为字符串资源,替换Adapter里的硬编码值,方便后续多语言适配

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 23:48:39