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

结合Retrofit实现嵌套RecyclerView:数据存储传递问题求助

解决嵌套RecyclerView的TMDB API数据存储与适配问题

核心思路

你需要将每个分类API返回的电影列表,封装成Category对象(包含分类名称+对应电影集合),统一存入数据源列表后传递给父适配器。关键是处理异步API请求的结果收集,并确保线程安全地更新UI。


步骤1:确认实体类与API接口正确性

首先确保你的数据类和Retrofit接口与TMDB返回格式匹配:

// TMDB单分类响应实体
data class MovieResponse(
    val results: List<Result>,
    val page: Int,
    val total_pages: Int
)

// 父分类实体
data class Category(val categoryName: String, val movies: List<Result>)

// Retrofit API接口(推荐用协程简化异步)
interface TmdbApi {
    @GET("movie/popular")
    suspend fun getPopularMovies(@Query("api_key") apiKey: String): Response<MovieResponse>

    @GET("movie/upcoming")
    suspend fun getUpcomingMovies(@Query("api_key") apiKey: String): Response<MovieResponse>
}

步骤2:安全收集多API请求结果

方式1:逐个请求并实时更新列表

适合不需要等待所有请求完成的场景,每获取一个分类数据就更新一次UI:

// 全局数据源(父适配器绑定的列表)
private val categoryList = mutableListOf<Category>()
private lateinit var parentAdapter: ParentAdapter

// 初始化适配器与RecyclerView
private fun initRecyclerView() {
    parentAdapter = ParentAdapter(categoryList)
    parentRv.apply {
        layoutManager = LinearLayoutManager(this@MainActivity)
        adapter = parentAdapter
    }
}

// 获取热门电影并添加到分类列表
private fun fetchPopularMovies() {
    lifecycleScope.launch {
        val response = tmdbApi.getPopularMovies(BuildConfig.TMDB_API_KEY)
        if (response.isSuccessful) {
            response.body()?.results?.let { movies ->
                val category = Category("热门电影", movies)
                // 主线程更新UI
                runOnUiThread {
                    categoryList.add(category)
                    parentAdapter.notifyItemInserted(categoryList.size - 1)
                }
            }
        }
    }
}

// 获取即将上映电影(逻辑同上)
private fun fetchUpcomingMovies() {
    lifecycleScope.launch {
        val response = tmdbApi.getUpcomingMovies(BuildConfig.TMDB_API_KEY)
        if (response.isSuccessful) {
            response.body()?.results?.let { movies ->
                val category = Category("即将上映", movies)
                runOnUiThread {
                    categoryList.add(category)
                    parentAdapter.notifyItemInserted(categoryList.size - 1)
                }
            }
        }
    }
}

// 在onCreate中调用初始化与请求
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    initRecyclerView()
    fetchPopularMovies()
    fetchUpcomingMovies()
}

方式2:等待所有请求完成后统一更新

适合需要保证所有分类数据加载完成再展示的场景,用协程async/await实现:

private fun fetchAllCategories() {
    lifecycleScope.launch {
        // 并发请求所有分类API
        val popularDeferred = async { tmdbApi.getPopularMovies(BuildConfig.TMDB_API_KEY) }
        val upcomingDeferred = async { tmdbApi.getUpcomingMovies(BuildConfig.TMDB_API_KEY) }

        // 等待所有请求返回
        val popularResp = popularDeferred.await()
        val upcomingResp = upcomingDeferred.await()

        // 整理分类数据
        val newCategoryList = mutableListOf<Category>()
        popularResp.body()?.results?.let {
            newCategoryList.add(Category("热门电影", it))
        }
        upcomingResp.body()?.results?.let {
            newCategoryList.add(Category("即将上映", it))
        }

        // 统一更新适配器
        runOnUiThread {
            categoryList.clear()
            categoryList.addAll(newCategoryList)
            parentAdapter.notifyDataSetChanged()
        }
    }
}

步骤3:父适配器正确绑定子RecyclerView

确保父适配器在onBindViewHolder中为子RecyclerView设置适配器与数据:

class ParentAdapter(private val categoryList: List<Category>) : RecyclerView.Adapter<ParentAdapter.CategoryViewHolder>() {

    inner class CategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvCategory: TextView = itemView.findViewById(R.id.tv_category_title)
        val rvMovies: RecyclerView = itemView.findViewById(R.id.rv_movies)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_category, parent, false)
        return CategoryViewHolder(view)
    }

    override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
        val category = categoryList[position]
        holder.tvCategory.text = category.categoryName
        
        // 初始化子RecyclerView
        holder.rvMovies.apply {
            layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
            adapter = MovieAdapter(category.movies)
        }
    }

    override fun getItemCount() = categoryList.size
}

关键注意事项

  • 网络请求必须在非主线程执行,用lifecycleScope可自动绑定页面生命周期,避免内存泄漏。
  • 处理API请求失败情况:可添加空数据占位、错误提示,或重试逻辑。
  • 避免频繁调用notifyDataSetChanged(),优先用notifyItemInserted()/notifyItemRangeChanged()提升性能。

内容的提问来源于stack exchange,提问作者Hrithik Rajput

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 10:55:54