结合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
相关产品推荐
相关产品推荐

