如何为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
相关产品推荐
相关产品推荐

