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

RecyclerView无法显示API返回数据的问题排查求助

Android RecyclerView 无法展示API数据排查与解决

我正在开发Android应用,尝试从API获取分类数据并在HomeFragment的RecyclerView中展示。已配置网络权限,添加了Coroutines、Retrofit、Picasso相关依赖,Logcat显示所有条件合法、适配器已调用,但RecyclerView始终无法显示数据。请帮忙排查可能的问题并提供解决方法。


相关代码

HomeFragment文件

import retrofit2.HttpException
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.ambrosia.Adaptors.catAdap
import com.example.ambrosia.RetroInstance
import com.example.ambrosia.databinding.FragmentHomeBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.IOException


class HomeFragment : Fragment() {
    private var _binding: FragmentHomeBinding? = null
    private val binding get() = _binding!!
    private lateinit var myAdapter: catAdap
    private lateinit var rv :RecyclerView
    private val job = Job()
    private val coroutineScope = CoroutineScope(Dispatchers.Main + job)
    private var isDataLoaded = false // Track if data has been loaded

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentHomeBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        rv = binding.rvCategory
        rv.layoutManager =
            LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
        myAdapter = catAdap(this@HomeFragment, emptyList())
        rv.adapter = myAdapter
        if (!isDataLoaded){
            Log.d("homefragment", "onViewCreated: fetchcategories called sucessfully")
            fetchCategories()
        }
    }

    private fun fetchCategories() {
        coroutineScope.launch {
            val response = try {
                RetroInstance.api.getCategory()
            } catch (e: HttpException) {
                Log.e("HomeFragment", "Http Error: ${e.message}", e)
                withContext(Dispatchers.Main) {
                    Toast.makeText(requireContext(), "Network Error", Toast.LENGTH_SHORT).show()
                }
                return@launch
            } catch (e: IOException) {
                Log.e("HomeFragment", "I/O Error: ${e.message}", e)
                withContext(Dispatchers.Main) {
                    Toast.makeText(requireContext(), "Network Error", Toast.LENGTH_SHORT).show()
                }
                return@launch
            } catch (e: Exception) {
                Log.e("HomeFragment", "Generic Error: ${e.message}", e)
                withContext(Dispatchers.Main) {
                    Toast.makeText(requireContext(), "An Error Occured", Toast.LENGTH_SHORT).show()
                }
                return@launch
            }

            withContext(Dispatchers.Main) {
                if (response.categories.isNotEmpty()) {
                    Log.d("HomeFragment", "Category list size: ${response.categories.size}")
                    myAdapter.catlist = response.categories
                    Log.d("HomeFragment", "after cat;ist called line 2")
                    myAdapter.notifyDataSetChanged()
                    Log.d("HomeFragment", "after notifydtasetchange")
                    myAdapter.onItemClick = { category ->
                        // Handle item click here
                        Log.d("HomeFragment", "Clicked on category: ${category.strCategory}")
                    }
                } else {
                    Log.w("HomeFragment", "Category list is empty")
                    Toast.makeText(requireContext(), "No Categories Found", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
        job.cancel()
    }
}

RecyclerView适配器文件

package com.example.ambrosia.Adaptors

import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.example.ambrosia.Models.Category
import com.example.ambrosia.R
import com.squareup.picasso.Picasso


class catAdap(val context: Fragment, var catlist: List<Category>) :
    RecyclerView.Adapter<catAdap.MyViewHolder>(){

    lateinit var onItemClick: ((Category) -> Unit)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(context.requireContext())
            .inflate(R.layout.itemdefine, parent, false) // Inflate your item layout
        return MyViewHolder(itemView)
    }

    override fun getItemCount(): Int {
        return catlist.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem =catlist[position]

        Log.d("TAG", "onBindViewHolder: item displayed succesfully")
        Picasso.get()
            .load(currentItem.strCategoryThumb)
            .into(holder.img)
        Log.d("TAG", "onBindViewHolder: item displayed after picasso")

        holder.itemView.setOnClickListener {
            onItemClick.invoke(currentItem)
        }
    }

    class MyViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView) {
        val img :ImageView
        init {
            img = itemView.findViewById(R.id.rvImg)
        }
    }
}

可能的问题与解决方法

1. Item布局未设置有效宽高

适配器仅加载图片到ImageView,但如果itemdefine.xml中的ImageView未设置明确宽高(比如用wrap_content但图片加载前尺寸为0),RecyclerView的Item会因高度为0而无法显示。

解决方法:
修改itemdefine.xml,给ImageView设置固定宽高或自适应规则:

<ImageView
    android:id="@+id/rvImg"
    android:layout_width="120dp"
    android:layout_height="120dp"
    android:scaleType="centerCrop"/>

2. Retrofit数据模型解析错误

如果Category类或外层响应模型的字段名与API返回JSON不匹配,会导致response.categories数据为空或字段值无效。

验证与解决:

  • 在fetchCategories中打印具体字段值,确认数据是否正确解析:
    Log.d("HomeFragment", "First category thumb: ${response.categories[0].strCategoryThumb}")
    
  • 确保模型类字段与JSON一致,或用@SerializedName映射:
    data class Category(
        @SerializedName("strCategory") val strCategory: String,
        @SerializedName("strCategoryThumb") val strCategoryThumb: String
    )
    
    data class CategoryResponse(
        @SerializedName("categories") val categories: List<Category>
    )
    
  • 确认Retrofit接口定义正确:
    interface MealApi {
        @GET("categories.php")
        suspend fun getCategory(): CategoryResponse
    }
    

3. RecyclerView布局属性错误

检查fragment_home.xml中RecyclerView的宽高设置,如果是wrap_content且Item高度为0,RecyclerView会显示为空。

解决方法:
设置RecyclerView宽高为有效范围:

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rvCategory"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>

4. 适配器点击事件初始化时机问题

当前代码在notifyDataSetChanged后才设置onItemClick,如果Item绑定时尚未初始化点击事件,可能引发异常(虽未在Log中显示,但会影响Item正常渲染)。

解决方法:
提前初始化点击事件:

// 在HomeFragment的onViewCreated中初始化适配器后立即设置
myAdapter.onItemClick = { category ->
    Log.d("HomeFragment", "Clicked on category: ${category.strCategory}")
}

// 或在适配器中添加空判断避免崩溃
holder.itemView.setOnClickListener {
    if (::onItemClick.isInitialized) {
        onItemClick.invoke(currentItem)
    }
}

5. 补充:标记数据加载状态

当前代码未在数据加载完成后将isDataLoaded设为true,Fragment重建时会重复请求,虽不影响当前显示,但建议修复:

withContext(Dispatchers.Main) {
    if (response.categories.isNotEmpty()) {
        // ... 现有代码
        isDataLoaded = true // 添加此行
    }
}

内容的提问来源于stack exchange,提问作者Jatin Kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:57:02