Android Studio 2023:如何正确将Retrofit响应列表传入Adapter?
问题:Retrofit获取数据后无法在RecyclerView显示列表
通过Retrofit成功获取API响应(日志验证响应体内容正确),但将数据传入使用DiffUtils的Adapter后,RecyclerView始终为空。仅在Adapter构造时传入列表能正常显示,但希望通过submitList的方式实现数据更新。
MainActivity代码
class QuizMainActivity : AppCompatActivity() { private lateinit var quizCategoryAdapter: QuizCategoryAdapter private lateinit var binding: ActivityQuizMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityQuizMainBinding.inflate(layoutInflater) setContentView(binding.root) hideactionbar() setupRecyclerView() lifecycleScope.launchWhenCreated { val response = try{ RetrofitInstance.api.getQuizCategories() }catch (e:IOException){ Log.d("QuizCatTag", "IOException"+e) return@launchWhenCreated }catch (e: HttpException){ Log.d("QuizCatTag", "HTTPException"+e) return@launchWhenCreated } if(response.isSuccessful && response.body()!=null){ setupRecyclerView() quizCategoryAdapter.quizCategoryList = response.body()!!.category_detail // 传入响应列表到Adapter的位置 Log.d("quizCategoryList", quizCategoryAdapter.quizCategoryList.toString() ) }else{ Log.d("TASG", "Response not successful") } } } private fun hideactionbar() { supportActionBar?.hide() } private fun setupRecyclerView() = binding.quizCategoryRv.apply { quizCategoryAdapter = QuizCategoryAdapter(this@QuizMainActivity) adapter = quizCategoryAdapter layoutManager = LinearLayoutManager(this@QuizMainActivity) } }
Adapter代码(使用DiffUtils处理数据变更)
class QuizCategoryAdapter( private val context: Context ) : RecyclerView.Adapter<QuizCategoryAdapter.QuizCategoryHolder>() { private val diffCallBack = object: DiffUtil.ItemCallback<CategoryDetail>(){ override fun areItemsTheSame(oldItem: CategoryDetail, newItem: CategoryDetail): Boolean { return oldItem.category_name == newItem.category_name } override fun areContentsTheSame(oldItem: CategoryDetail, newItem: CategoryDetail): Boolean { return oldItem == newItem } } private val differ = AsyncListDiffer(this, diffCallBack) var quizCategoryList : List<CategoryDetail> get() = differ.currentList set(value) {differ.submitList(value)} val textList = quizCategoryList.chunked(3) class QuizCategoryHolder(binding: QuizCategoryItemBinding) : RecyclerView.ViewHolder(binding.root){ val listTextV = listOf(binding.qciTv1, binding.qciTv2, binding.qciTv3) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuizCategoryAdapter.QuizCategoryHolder { return QuizCategoryHolder(QuizCategoryItemBinding.inflate(LayoutInflater.from(context), parent,false)) } override fun getItemCount(): Int { return textList.size } override fun onBindViewHolder(holder: QuizCategoryHolder, position: Int) { for (i in 0 until textList[position].size){ if(textList[position][i].category_name==""){ holder.listTextV[i].isVisible = false }else{ holder.listTextV[i].text = textList[position][i].category_name } } holder.itemView.setOnClickListener{ val intent = Intent(context, QuizQuestionActivity::class.java) context.startActivity(intent) } } }
问题分析与解决方案
核心问题
textList未动态更新:原代码中textList是普通val属性,仅在Adapter初始化时基于空列表执行一次chunked(3),后续quizCategoryList更新时,textList不会重新计算,导致getItemCount()始终返回0,RecyclerView无内容。- 重复创建Adapter:MainActivity请求成功后重复调用
setupRecyclerView(),会重新创建Adapter实例,覆盖原quizCategoryAdapter引用,导致数据传入的是新的空Adapter。
修复步骤
1. 将textList改为计算属性
修改Adapter中的textList定义,确保每次访问时都基于最新的列表重新分块:
val textList: List<List<CategoryDetail>> get() = quizCategoryList.chunked(3)
2. 移除MainActivity中重复的setupRecyclerView()调用
在请求成功的代码块中删除重复的初始化方法,保留原有的Adapter引用:
if(response.isSuccessful && response.body()!=null){ // 移除该行:setupRecyclerView() quizCategoryAdapter.quizCategoryList = response.body()!!.category_detail Log.d("quizCategoryList", quizCategoryAdapter.quizCategoryList.toString() ) }
3. 验证CategoryDetail的equals()实现
如果CategoryDetail不是Kotlin数据类,需要手动实现equals()方法,否则DiffUtils的areContentsTheSame无法正确判断内容是否变化。数据类会自动生成该方法,无需额外处理。
4. 优化onBindViewHolder逻辑(可选)
避免索引越界风险,优化视图显示逻辑:
override fun onBindViewHolder(holder: QuizCategoryHolder, position: Int) { val currentChunk = textList[position] // 先重置所有TextView可见性 holder.listTextV.forEach { it.isVisible = true } // 填充当前chunk的内容 currentChunk.forEachIndexed { index, category -> if (category.category_name.isBlank()) { holder.listTextV[index].isVisible = false } else { holder.listTextV[index].text = category.category_name } } // 隐藏超出chunk长度的TextView for (i in currentChunk.size until holder.listTextV.size) { holder.listTextV[i].isVisible = false } holder.itemView.setOnClickListener{ val intent = Intent(context, QuizQuestionActivity::class.java) context.startActivity(intent) } }
内容的提问来源于stack exchange,提问作者Dhruv194
相关产品推荐
相关产品推荐

