You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题图示

问题展示图


解决方法

  1. 修复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} کیلومتر"
        }
    }
    
  2. 确保RecyclerView布局测量正确
    在Fragment的onViewCreated中添加以下代码,帮助RecyclerView更准确地计算布局:

    binding.recyclerViewDistanceList.setHasFixedSize(true)
    binding.recyclerViewDistanceList.clipToPadding = false
    
  3. 检查LiveData的线程切换
    确保ViewModel中更新LiveData的操作在主线程执行,Retrofit回调如果在子线程,必须切换到主线程再更新数据:

    // ViewModel示例代码
    fun fetchDistanceList() {
        viewModelScope.launch(Dispatchers.IO) {
            val response = apiService.getDistanceList()
            withContext(Dispatchers.Main) {
                distanceListLiveData.postValue(response.body())
            }
        }
    }
    
  4. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:20:05