Android Kotlin多筛选器失效求助:多次筛选后列表无法重置
问题根源与修复方案
你的问题核心是每次筛选都基于上一次的筛选结果,而非原始完整数据源,加上代码里的几个逻辑问题,导致多次筛选后数据越筛越少甚至失效。结合你的代码,具体问题和修复步骤如下:
核心问题拆解
- Observer重复注册:把
viewModel.contacts.observe()放在菜单点击事件里,每次点菜单都会新增一个数据监听,后续数据变化时会多次触发筛选逻辑,导致数据混乱。 - 筛选依赖历史结果:每次筛选都是从
filteredList(上一次的筛选结果)开始过滤,而不是从原始的完整数据源重新计算,数据会越筛越窄直至失效。 - 搜索按钮监听重复绑定:每次Observer触发都会给
btSearch设置新的点击监听,多次触发后逻辑互相干扰。 - 排序逻辑错误:比如
filteredList.sortedBy { it.wins > myWins },这里返回的是布尔值,sortedBy会把所有true的项堆在前面,false的堆在后面,并不是按wins的数值大小排序。
修复后的代码实现
第一步:重构页面初始化逻辑(示例放在onViewCreated中)
// 保存原始完整数据源,确保每次筛选都从全量数据开始 private var originalContacts: List<Contact> = emptyList() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 只注册一次数据监听,保存原始全量数据 viewModel.contacts.observe(viewLifecycleOwner) { contacts -> originalContacts = contacts // 页面初始化时加载全部数据 updateRecyclerView(contacts) } // 搜索按钮只绑定一次点击事件,避免重复触发 binding.btSearch.setOnClickListener { performFilterAndSort() } // 弹窗菜单点击逻辑 popupMenu.setOnMenuItemClickListener { item -> when (item.itemId) { R.id.option1 -> { binding.ddbtSearch.text = "Matches" binding.ddbtLvl.visibility = View.VISIBLE true } R.id.option2 -> { binding.ddbtSearch.text = "Clubs" binding.ddbtLvl.visibility = View.GONE viewModel.clubdatabase.observe(viewLifecycleOwner) { clubs -> binding.rvFinderResults.adapter = ClubAdapter(clubs, viewModel) } true } else -> false } } popupMenu.show() }
第二步:抽离筛选与排序的独立函数
private fun performFilterAndSort() { // 每次筛选都从原始全量数据开始 var filteredList = originalContacts.toMutableList() // 按等级筛选 val selectedLevel = binding.ddbtLvl.text.toString() val validLevels = listOf("BEGINNER", "IMPROVER", "ADVANCED", "PRACTITIONER", "EXPERT") if (selectedLevel in validLevels) { filteredList = filteredList.filter { it.level == selectedLevel }.toMutableList() } // 按运动类型筛选 val selectedSport = binding.ddBtSports.text.toString() val validSports = listOf("BADMINTON", "SQUASH", "TISCHTENNIS", "TENNIS", "FUSSBALL", "HOCKEY", "CRICKET", "HANDBALL") if (selectedSport in validSports) { filteredList = filteredList.filter { it.sportsOne == selectedSport }.toMutableList() } // 修正排序逻辑 val selectedSort = binding.ddBtSort.text.toString() filteredList = when (selectedSort) { "ENTFERNUNG" -> filteredList.sortedBy { it.entfernung }.toMutableList() "DATUM" -> filteredList.sortedBy { it.date }.toMutableList() // 按wins降序排列(如果需求是赢的多的在前) "WINS" -> filteredList.sortedByDescending { it.wins }.toMutableList() // 按年龄升序排列,可根据需求改成sortedByDescending "ALTER" -> filteredList.sortedBy { it.age }.toMutableList() else -> filteredList } // 复用已有Adapter更新数据,比每次新建更高效 val adapter = binding.rvFinderResults.adapter as? FinderResultAdapter if (adapter != null) { adapter.updateData(filteredList) } else { binding.rvFinderResults.adapter = FinderResultAdapter(filteredList, viewModel) } } // 需要在FinderResultAdapter中添加更新数据的方法 // 示例Adapter内的代码: // class FinderResultAdapter(private var list: List<Contact>, private val viewModel: YourViewModel) : RecyclerView.Adapter<...>() { // ... // fun updateData(newList: List<Contact>) { // this.list = newList // notifyDataSetChanged() // } // }
额外优化建议
- 把筛选条件的字符串(比如等级、运动类型)定义成常量,避免硬编码错误,比如
const val LEVEL_BEGINNER = "BEGINNER"。 - 如果排序需求是“和我的wins/年龄对比排序”,可以改成
sortedByDescending { it.wins - myWins },按差值大小排序更符合逻辑。 - 可以给下拉选择框添加选中监听,实时触发筛选,不用依赖搜索按钮点击。
内容的提问来源于stack exchange,提问作者LauraN
相关产品推荐
相关产品推荐

