Android SearchView实现同义词关联搜索匹配项的方法
Android 语义关联搜索功能实现方案
原有add()方法不生效的原因
- 初始化
ArrayAdapter时传入的arrayOf()生成的是不可变数组,适配器本身不支持对该数据源做增删操作,直接调用add()要么抛出异常要么操作无响应 - 就算手动add成功,后续调用默认的
filter()方法时,系统会用初始原始数据源重新做字符串包含匹配,手动添加的项会被直接覆盖 - 原有分支判断存在逻辑冲突:多个if分支会先后触发多次filter操作,最后一次执行的filter结果会覆盖之前的所有操作
实现思路
- 提前定义每个列表项对应的语义触发关键词映射表,替代零散的if判断
- 自定义
ArrayAdapter的过滤逻辑,替换默认的简单字符串包含匹配规则,实现语义关联匹配 - 用可变列表作为适配器的数据源,支持动态更新展示内容
完整可运行代码
首先定义关键词映射和适配器:
// 语义映射配置:key为列表最终展示的项,value为所有可触发该项的关联关键词 val keywordMatchMap = mapOf( "distance" to listOf("kilometer", "mi", "distance"), "tax" to listOf("tax", "money", "bill"), "color" to listOf("hex", "rgb", "color") ) // 全量原始列表项 val fullAppletList = listOf("distance", "tax", "color") // 可变列表作为适配器的实际展示数据源 val displayList = ArrayList(fullAppletList) // 自定义适配器重写过滤逻辑 val appletAdapter = object : ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, displayList ) { override fun getFilter(): Filter { return object : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { val filterResult = FilterResults() val inputQuery = constraint?.toString()?.trim()?.lowercase() ?: "" // 输入为空时返回全量列表 if (inputQuery.isEmpty()) { filterResult.values = fullAppletList filterResult.count = fullAppletList.size return filterResult } // 遍历语义映射表,匹配所有符合条件的项 val matchedItems = mutableListOf<String>() for ((targetItem, triggerKeywords) in keywordMatchMap) { // 匹配规则:输入包含关键词/关键词包含输入即命中,不区分大小写 if (triggerKeywords.any { it.contains(inputQuery) || inputQuery.contains(it) }) { matchedItems.add(targetItem) } } filterResult.values = matchedItems filterResult.count = matchedItems.size return filterResult } @Suppress("UNCHECKED_CAST") override fun publishResults(constraint: CharSequence?, results: FilterResults?) { displayList.clear() val resultList = results?.values as? List<String> ?: emptyList() displayList.addAll(resultList) if (results != null && results.count > 0) { notifyDataSetChanged() } else { notifyDataSetInvalidated() } } } } }
然后绑定SearchView监听,原有冗余判断全部删除:
binding.appletList.adapter = appletAdapter binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { binding.searchView.clearFocus() return false } override fun onQueryTextChange(newText: String): Boolean { // 直接触发自定义过滤器即可,不需要额外写contains判断 appletAdapter.filter.filter(newText.trim()) return false } })
规则调整说明
- 当前匹配规则为模糊匹配,比如输入
kil即可命中kilometer关联的distance项,输入rg即可命中rgb关联的color项 - 如果需要严格匹配完整关键词,只需要把
performFiltering方法里的匹配条件替换为triggerKeywords.any { it == inputQuery }即可 - 如果后续需要新增关联项,只需要在
keywordMatchMap里加对应配置即可,不需要修改过滤逻辑
内容的提问来源于stack exchange,提问作者Marvil
相关产品推荐
相关产品推荐

