Kotlin Android中AlertDialog无法更新列表:筛选后排序失效问题
解决筛选后无法再次排序及筛选列表为空的问题
核心问题分析
- 重复观察LiveData:每次点击筛选按钮都调用
viewModel.products.observe,会累积多个观察者,导致数据处理逻辑混乱;且LiveData回调是异步的,直接在回调里操作filteredList会因为时序问题拿不到最新数据。 - 初始数据加载错误:
onViewCreated中直接执行filteredList.addAll(adapter.recyclerListDiffer.currentList)时,viewModel.products还未完成回调,currentList为空,导致初始filteredList无数据。 - 筛选逻辑时序错误:在遍历
filterArrayBool的循环内判断selectedItemList.size,此时selectedItemList还未收集完所有选中的分类,筛选条件错误;且多分类筛选时存在条件遗漏(比如4个分类的筛选漏了第二个选项)。 - 状态维护缺失:没有保存当前的筛选状态,再次筛选后排序逻辑找不到正确的数据源;
filterArrayBool的重置逻辑无效(forEach { it==false }只是比较,未修改数组)。
修复后的代码
@AndroidEntryPoint class ProductFragment : Fragment(R.layout.fragment_product) { private var fragmentBinding: FragmentProductBinding? = null @Inject lateinit var viewModel: HomeViewModel @Inject lateinit var adapter: ProductsAdapter // 保存原始完整商品列表 private val originalProducts = mutableListOf<Product>() // 当前选中的筛选分类 private val selectedCategories = mutableSetOf<String>() // 当前排序类型 private var currentSortType = SortType.BEST_MATCH // 定义排序类型枚举,避免字符串判断的误差 private enum class SortType { BEST_MATCH, PRICE_ASC, PRICE_DESC } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val binding = FragmentProductBinding.bind(view) fragmentBinding = binding binding.productsRecycler.adapter = adapter binding.productsRecycler.layoutManager = StaggeredGridLayoutManager(2, RecyclerView.VERTICAL) // 只观察一次LiveData,获取原始数据 viewModel.getAllProducts() viewModel.products.observe(viewLifecycleOwner) { response -> response.data?.let { products -> originalProducts.clear() originalProducts.addAll(products) // 首次加载时应用默认的筛选和排序 updateDisplayList() } } adapter.setOnItemClickListener { val action = ProductFragmentDirections.actionProductFragmentToDetailScreenFragment(it) findNavController().navigate(action) } // 返回按钮 binding.productsBackbutton.setOnClickListener { findNavController().popBackStack() } // 排序按钮逻辑 val sortArray = arrayOf("Best Match", "Price Asc", "Price Desc") binding.productsSortbutton.setOnClickListener { var selectedIndex = when (currentSortType) { SortType.BEST_MATCH -> 0 SortType.PRICE_ASC -> 1 SortType.PRICE_DESC -> 2 } MaterialAlertDialogBuilder(requireContext()) .setSingleChoiceItems(sortArray, selectedIndex) { _, which -> selectedIndex = which } .setPositiveButton("Sort") { _, _ -> currentSortType = when (selectedIndex) { 0 -> SortType.BEST_MATCH 1 -> SortType.PRICE_ASC 2 -> SortType.PRICE_DESC else -> SortType.BEST_MATCH } updateDisplayList() binding.productsRecycler.smoothScrollToPosition(0) } .show() } // 筛选按钮逻辑 val filterArray = arrayOf("Electronics", "Jewelery", "Men's Clothing", "Women's Clothing") val filterArrayBool = booleanArrayOf(false, false, false, false) binding.productsFilterbutton.setOnClickListener { // 更新复选框状态为当前选中的分类 filterArray.forEachIndexed { index, category -> filterArrayBool[index] = selectedCategories.contains(category.lowercase()) } MaterialAlertDialogBuilder(requireContext()) .setCancelable(false) .setMultiChoiceItems(filterArray, filterArrayBool) { _, which, isChecked -> val category = filterArray[which].lowercase() if (isChecked) { selectedCategories.add(category) } else { selectedCategories.remove(category) } } .setPositiveButton("FILTER") { _, _ -> updateDisplayList() binding.productsRecycler.smoothScrollToPosition(0) } .setNeutralButton("Show All") { _, _ -> selectedCategories.clear() filterArrayBool.fill(false) updateDisplayList() binding.productsRecycler.smoothScrollToPosition(0) } .show() } } // 统一处理筛选和排序,生成要显示的列表 private fun updateDisplayList() { var displayList = originalProducts.toList() // 应用筛选 if (selectedCategories.isNotEmpty()) { displayList = displayList.filter { product -> selectedCategories.contains(product.category) } } // 应用排序 displayList = when (currentSortType) { SortType.PRICE_ASC -> displayList.sortedBy { it.price.toDouble() } SortType.PRICE_DESC -> displayList.sortedByDescending { it.price.toDouble() } SortType.BEST_MATCH -> displayList // 原始顺序即为最佳匹配 } // 提交给Adapter adapter.recyclerListDiffer.submitList(displayList) } override fun onDestroyView() { super.onDestroyView() fragmentBinding = null } }
关键修改说明
- 统一数据处理逻辑:新增
updateDisplayList()方法,基于原始数据统一处理筛选和排序,避免多次操作数据源导致的混乱。 - 状态维护:用
selectedCategories保存当前选中的筛选分类,currentSortType保存当前排序类型,确保每次操作都基于最新状态。 - 避免重复观察:只在
onViewCreated中观察一次viewModel.products,拿到原始数据后保存,后续所有操作都基于这份原始数据。 - 修复筛选逻辑:先收集所有选中的分类,再统一筛选;修复多分类筛选的条件遗漏问题。
- 优化排序逻辑:用枚举类替代字符串判断,避免拼写错误;弹窗初始化时自动选中当前排序类型,提升用户体验。
- 修复状态重置:用
filterArrayBool.fill(false)正确重置复选框状态。
内容的提问来源于stack exchange,提问作者Calaquessir
相关产品推荐
相关产品推荐

