Search code examples
androidkotlinandroid-recyclerviewretrofitandroid-adapter

How to correctly pass a list to Adapter from the Retrofit Response? [Android Studio 2023]


So, I have followed many YouTube tutorials to correctly learn about Retrofit in Android and how to pass data from the response to my adapter and display the list but none of them is working for me right now and I don't know why.

In my MainActivity I am calling the RetrofitInstance and hence initiating a GET request to my API and fetching the response from it. When I debug my code and Log the response the body is correctly visible as it should be, but the problem occurs when I try to pass that very response in the form of list to my adapter.

Here's the code of both the files -

MainActivity code -

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 //place where I am sending my response list to my 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 code - (I am also using DiffUtils in my adapter for handling the change in data)

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)
            }

    }

}

I tried to display the list that I am fetching from API through Retrofit into my RecyclerView Adapter. But unfortunately, when I pass the list to my Adapter class it still shows as null/empty and does not display anything.

Having said that when I tried to pass the list as a parameter to class then it displayed the list. And in my opinion, I don't think that this is right way to do it.

Please guide me and let me know your thoughts. What is utmost correct way for achieving this?


Solution

  • So I debugged and Log.Cat everything in my code and finally found where the error was -

    Basically there was actually no problem with the list. The list was going to the adapter as it should. But in my adapter class I was chunking the list as you can see

    val textList  = quizCategoryList.chunked(3)
    

    Now this particular line and textList was getting null because this list was not getting updated when I was finally fetching the list from API. Hence causing the error of not displaying anything.

       override fun getItemCount(): Int {
     return textList.size // was showing 0 hence not displaying anything }
    

    So to fix this error I just chunked the list in the onBindViewHolder method before displaying and also edited my getItemCount() method.

    The updated code of the Adapter class looks something like this -

    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)}
    
     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 quizCategoryList.chunked(3).size //returning the size of the chunked list so that content is rightfully displayed
     }
    
     override fun onBindViewHolder(holder: QuizCategoryHolder, position: Int) {
       
    
         val textList  = quizCategoryList.chunked(3)  //chunking my list here rather than at the beginning of the class where it was not getting updated. 
    
         for (i in 0 until textList[position].size){
    
                 holder.listTextV[i].text = textList[position][i].category_name
         }
    
                holder.itemView.setOnClickListener{
                 val intent = Intent(context, QuizQuestionActivity::class.java)
                 context.startActivity(intent)
             }
    
     }
    
    
    
    }