Kotlin中RecyclerView需滚动才显示Item问题求助
RecyclerView初始不显示列表,滚动后才加载内容问题
我通过LiveData结合Retrofit从服务器拉取数据,用RecyclerView展示列表,Adapter采用ViewBinding实现。当前遇到的问题是:RecyclerView初始化后不显示任何列表项,只有手动滚动后才会加载展示内容,试过多种方案都没解决。
Fragment代码
class DistanceListFragment : BaseFragment() { private var _binding: FragmentDistanceListBinding? = null private val binding get() = _binding!! private val distanceListViewModel : DistanceListViewModel by viewModel() private val distanceListAdapter = DistanceListAdapter() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentDistanceListBinding.inflate(inflater, container, false) val view = binding.root return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.recyclerViewDistanceList.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false) binding.recyclerViewDistanceList.adapter = distanceListAdapter prepareDistanceListViewModel() } private fun prepareDistanceListViewModel(){ distanceListViewModel.distanceListLiveData.observe(viewLifecycleOwner){ distanceListAdapter.distanceList = it as ArrayList<Distance> } } override fun onDestroyView() { super.onDestroyView() _binding = null } }
Adapter代码
class DistanceListAdapter : RecyclerView.Adapter<DistanceListAdapter.ViewHolder>() { var distanceList = ArrayList<Distance>() set(value) { field = value notifyDataSetChanged() } inner class ViewHolder(private val binding: ItemDistanceListBinding) : RecyclerView.ViewHolder(binding.root) { fun bindDistance(distance: Distance) { Timber.i(distance.beginning) binding.textViewDistanceListBeginning.text = distance.beginning binding.textViewDistanceListDestination.text = distance.destination binding.textViewDistanceListDistance.text = "${distance.distance} کیلومتر" } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder( ItemDistanceListBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) } override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bindDistance(distanceList[position]) override fun getItemCount(): Int = distanceList.size }
Fragment布局文件
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView_distanceList" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" /> </androidx.constraintlayout.widget.ConstraintLayout>
Adapter布局文件
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/background_item_distance_list" > <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_distanceList_topParent" style="@style/Guideline.All.TopParent" app:layout_constraintGuide_percent="0.02"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_distanceList_bottomParent" style="@style/Guideline.All.BottomParent" app:layout_constraintGuide_percent="0.95"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_distanceList_leftParent" style="@style/Guideline.All.LeftParent" app:layout_constraintGuide_percent="0.98"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_distanceList_rightParent" style="@style/Guideline.All.RightParent" app:layout_constraintGuide_percent="0.02"/> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline_distanceList_destinationItemsRightSide" style="@style/Guideline.All.Vertical" app:layout_constraintGuide_percent="0.58" /> <TextView android:id="@+id/textView_distanceList_beginningText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dp" app:layout_constraintStart_toStartOf="@id/guideline_distanceList_leftParent" app:layout_constraintEnd_toEndOf="@+id/guideline_distanceList_rightParent" app:layout_constraintTop_toTopOf="@id/guideline_distanceList_topParent" app:layout_constraintHorizontal_bias="0.0" android:text="مبدا" /> <TextView android:id="@+id/textView_distanceList_beginning" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" app:layout_constraintStart_toStartOf="@id/guideline_distanceList_leftParent" app:layout_constraintTop_toBottomOf="@+id/textView_distanceList_beginningText" tools:text="بیرجند" /> <TextView android:visibility="gone" android:id="@+id/textView_distanceList_destinationText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dp" app:layout_constraintStart_toStartOf="@id/guideline_distanceList_destinationItemsRightSide" app:layout_constraintTop_toTopOf="@id/guideline_distanceList_topParent" android:text="مقصد" /> <TextView android:visibility="gone" android:id="@+id/textView_distanceList_destination" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" app:layout_constraintStart_toStartOf="@id/guideline_distanceList_destinationItemsRightSide" app:layout_constraintTop_toBottomOf="@+id/textView_distanceList_destinationText" tools:text="ایرانشهر" /> <TextView android:visibility="gone" android:id="@+id/textView_distanceList_distance" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="16sp" app:layout_constraintEnd_toEndOf="@id/guideline_distanceList_rightParent" app:layout_constraintBottom_toBottomOf="@+id/textView_distanceList_destination" android:text="795 کیلومتر"/> </androidx.constraintlayout.widget.ConstraintLayout>
问题图示

解决方法
修复Adapter布局的可见性问题
你Adapter布局里的目的地相关TextView默认设置为gone,如果Item的高度依赖这些隐藏的View,会导致RecyclerView初始无法正确计算Item高度,滚动后才会重新测量。建议在bindDistance方法里根据数据动态控制可见性:fun bindDistance(distance: Distance) { Timber.i(distance.beginning) binding.textViewDistanceListBeginning.text = distance.beginning // 根据数据动态显示/隐藏目的地组件 val hasDestination = !distance.destination.isNullOrEmpty() binding.textViewDistanceListDestinationText.visibility = if (hasDestination) View.VISIBLE else View.GONE binding.textViewDistanceListDestination.visibility = if (hasDestination) View.VISIBLE else View.GONE binding.textViewDistanceListDistance.visibility = if (hasDestination) View.VISIBLE else View.GONE if (hasDestination) { binding.textViewDistanceListDestination.text = distance.destination binding.textViewDistanceListDistance.text = "${distance.distance} کیلومتر" } }确保RecyclerView布局测量正确
在Fragment的onViewCreated中添加以下代码,帮助RecyclerView更准确地计算布局:binding.recyclerViewDistanceList.setHasFixedSize(true) binding.recyclerViewDistanceList.clipToPadding = false检查LiveData的线程切换
确保ViewModel中更新LiveData的操作在主线程执行,Retrofit回调如果在子线程,必须切换到主线程再更新数据:// ViewModel示例代码 fun fetchDistanceList() { viewModelScope.launch(Dispatchers.IO) { val response = apiService.getDistanceList() withContext(Dispatchers.Main) { distanceListLiveData.postValue(response.body()) } } }优化Adapter的刷新逻辑
替换notifyDataSetChanged为更精准的刷新方法,避免全局刷新可能带来的异常:var distanceList = ArrayList<Distance>() set(value) { val oldSize = field.size field.clear() field.addAll(value) notifyItemRangeInserted(oldSize, value.size) }
内容的提问来源于stack exchange,提问作者Nima Khalili
相关产品推荐
相关产品推荐

