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

如何为TabLayout或底部导航的所有Fragment实现通用SearchView

适配TabLayout多Fragment的通用SearchView实现方案

核心思路是将搜索事件从宿主Activity统一分发到当前选中的Tab Fragment,每个Fragment独立处理内部RecyclerView的过滤逻辑,模块间耦合度低,新增Tab无需修改原有逻辑。

1. 定义统一搜索回调接口

先定义公共接口,所有需要支持搜索的Tab Fragment都要实现该接口,统一搜索事件的入口:

interface SearchableFragment {
    // 搜索词变化时触发,返回true表示当前Fragment已处理该搜索事件
    fun onSearchQueryChanged(query: String): Boolean
}

2. 宿主Activity实现SearchView配置与事件分发

在宿主Activity中初始化SearchView,监听搜索词变化,将事件分发到当前选中的Fragment:

2.1 搜索功能初始化

先在Menu资源中定义SearchView条目,然后在Activity中加载Menu并绑定监听:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.menu_main_search, menu)
    val searchItem = menu.findItem(R.id.action_search)
    val searchView = searchItem.actionView as SearchView

    // 监听搜索输入变化
    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
        override fun onQueryTextSubmit(query: String?): Boolean {
            dispatchSearchQuery(query.orEmpty())
            return true
        }

        override fun onQueryTextChange(newText: String?): Boolean {
            dispatchSearchQuery(newText.orEmpty())
            return true
        }
    })
    return super.onCreateOptionsMenu(menu)
}

2.2 搜索事件分发逻辑

private fun dispatchSearchQuery(query: String) {
    // 获取ViewPager当前选中的Fragment,根据你使用的ViewPager/ViewPager2调整获取逻辑
    val currentFragment = supportFragmentManager.findFragmentByTag("f${viewPager2.currentItem}")
    // 如果当前Fragment实现了搜索接口,就分发搜索词
    if (currentFragment is SearchableFragment) {
        currentFragment.onSearchQueryChanged(query)
    }
}

注:ViewPager2的FragmentStateAdapter默认生成的Fragment tag格式为f${position},如果是自定义适配器可以自行调整Fragment获取逻辑。

3. Tab Fragment实现搜索逻辑

每个Tab的Fragment实现SearchableFragment接口,将搜索词传递给自身的RecyclerView Adapter执行过滤:

class FirstTabFragment : Fragment(), SearchableFragment {
    private lateinit var recyclerAdapter: FirstTabItemAdapter

    override fun onSearchQueryChanged(query: String): Boolean {
        recyclerAdapter.filter.filter(query)
        return true
    }

    // 其余Fragment初始化、RecyclerView绑定逻辑省略
}

4. RecyclerView Adapter实现过滤逻辑

Adapter实现Android原生Filterable接口,自行定义条目过滤规则,内部维护原始全量数据和当前展示的过滤后数据:

class FirstTabItemAdapter(private val originalData: List<ItemBean>) : 
    RecyclerView.Adapter<FirstTabItemAdapter.ViewHolder>(), Filterable {

    // 实际展示在RecyclerView中的数据
    private val showData = mutableListOf<ItemBean>()

    init {
        // 默认展示全量原始数据
        showData.addAll(originalData)
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            // 后台线程执行过滤逻辑
            override fun performFiltering(constraint: CharSequence?): FilterResults {
                val query = constraint.toString().trim()
                val filteredList = if (query.isEmpty()) {
                    // 搜索词为空时返回全量数据
                    originalData
                } else {
                    // 自定义过滤规则,示例为匹配标题或内容,忽略大小写
                    originalData.filter { item ->
                        item.title.contains(query, ignoreCase = true) 
                            || item.content.contains(query, ignoreCase = true)
                    }
                }
                return FilterResults().apply { values = filteredList }
            }

            // 主线程更新UI
            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                showData.clear()
                showData.addAll(results?.values as? List<ItemBean> ?: emptyList())
                notifyDataSetChanged()
            }
        }
    }

    // 其余Adapter常规逻辑:onCreateViewHolder、onBindViewHolder等,绑定数据全部使用showData
}

可选优化点

  • 切换Tab自动适配已有搜索词:监听TabLayout选中事件,切换Tab时主动将当前SearchView已输入的内容分发给新选中的Fragment,无需用户重新输入
  • 输入防抖:给SearchView的输入监听加300ms左右的防抖逻辑,减少频繁过滤造成的卡顿
  • 空状态适配:过滤结果为空时展示对应空提示,优化用户体验
  • 搜索状态保存:每个Fragment自行存储当前搜索词,切换Tab时不需要重新执行过滤逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 08:06:03