Android Fragment中LiveData数据不显示,旋转屏幕后正常的问题求助
问题排查与解决方案
核心问题分析
你的代码存在两个关键问题导致RecyclerView初始不显示数据:
1. RecyclerView未设置LayoutManager
RecyclerView必须指定LayoutManager才能确定子项的排列布局方式(线性、网格等),你的MainPageFragment中仅为RecyclerView设置了Adapter,但未设置LayoutManager,这会导致RecyclerView无法渲染任何子项。
2. Adapter数据更新未通知RecyclerView
ShopAppRecyclerViewAdapter中直接给products属性赋值,但没有调用notifyDataSetChanged()(或相关通知方法),RecyclerView无法感知数据变化,因此不会更新UI。
而屏幕旋转后能显示的原因:
- 配置变更后Fragment重建,ViewModel保留了之前请求到的数据,Observer订阅LiveData时会立刻收到已有数据;
- 重建后的RecyclerView在绑定Adapter后,会触发一次初始布局(此时Adapter的
products已经有数据),但这只是巧合,并非正确的实现方式。
修复步骤
步骤1:为RecyclerView添加LayoutManager
修改MainPageFragment的onCreateView方法,为两个RecyclerView设置LayoutManager(这里以横向线性布局为例,可根据需求调整):
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentMainPageBinding.inflate(inflater, container, false) val recyclerviewLatestProducts = binding.recyclerViewLatestProduct val recyclerviewProductsOnDiscont = binding.recyclerViewProductsOnDiscont // 添加LayoutManager,指定布局方式 recyclerviewLatestProducts.layoutManager = LinearLayoutManager(requireContext(), LinearLayout.HORIZONTAL, false) recyclerviewProductsOnDiscont.layoutManager = LinearLayoutManager(requireContext(), LinearLayout.HORIZONTAL, false) val adapterLatestProduct = ShopAppRecyclerViewAdapter() val adapterProductsOnDiscont = ShopAppRecyclerViewAdapter() recyclerviewLatestProducts.adapter = adapterLatestProduct recyclerviewProductsOnDiscont.adapter = adapterProductsOnDiscont viewModel.getLatestProduct().observe(viewLifecycleOwner, { adapterLatestProduct.products = it // 通知RecyclerView数据更新 adapterLatestProduct.notifyDataSetChanged() }) viewModel.getProductOnDiscont().observe(viewLifecycleOwner) { adapterProductsOnDiscont.products = it // 通知RecyclerView数据更新 adapterProductsOnDiscont.notifyDataSetChanged() } viewModel.getDataAboutProductsFromNetwork() return binding.root }
步骤2:优化Adapter的数据更新逻辑(可选但推荐)
为了避免每次设置数据都手动调用通知方法,可修改Adapter的products属性,通过setter自动触发刷新:
class ShopAppRecyclerViewAdapter : RecyclerView.Adapter<ShopAppViewHolder>() { // 改为私有字段,通过setter控制更新逻辑 private var _products = listOf<Product>() var products: List<Product> get() = _products set(value) { _products = value notifyDataSetChanged() } // 其余原有代码保持不变... }
修改后,Fragment中只需执行adapterLatestProduct.products = it即可,无需手动调用notifyDataSetChanged()。
额外优化:使用DiffUtil提升性能(可选)
如果数据量较大,notifyDataSetChanged()会刷新整个列表,推荐使用DiffUtil实现局部刷新,提升性能:
class ShopAppRecyclerViewAdapter : RecyclerView.Adapter<ShopAppViewHolder>() { private var _products = listOf<Product>() var products: List<Product> get() = _products set(value) { val diffResult = DiffUtil.calculateDiff(ProductDiffCallback(_products, value)) _products = value diffResult.dispatchUpdatesTo(this) } // 实现DiffCallback,用于对比新旧数据 class ProductDiffCallback(private val oldList: List<Product>, private val newList: List<Product>) : DiffUtil.Callback() { override fun getOldListSize() = oldList.size override fun getNewListSize() = newList.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 根据Product的唯一标识判断是否为同一项目(示例用name,实际建议用id) val oldItem = oldList[oldItemPosition] val newItem = newList[newItemPosition] return when { oldItem is Product.Latest && newItem is Product.Latest -> oldItem.name == newItem.name oldItem is Product.FlashSale && newItem is Product.FlashSale -> oldItem.name == newItem.name else -> false } } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { // 判断内容是否完全一致,需确保Product类正确实现equals方法 return oldList[oldItemPosition] == newList[newItemPosition] } } // 其余原有代码保持不变... }
其他注意点
- 确保
Product类正确实现equals()方法(使用DiffUtil时需要),否则无法准确判断内容是否变化; - 可在ViewModel的
processData方法中添加日志,确认网络请求返回的数据是否有效、非空; - 检查Dagger2的注入逻辑,确保
component.inject(this)中的component已正确初始化。
内容的提问来源于stack exchange,提问作者Влад Михнёнок
相关产品推荐
相关产品推荐

