Jatin Kumar
Jatin Kumar

Reputation: 11

Displaying the items from API in Recycler view in Main Fragment

I have tried displaying the items from this API

API Link

https://www.themealdb.com/api/json/v1/1/categories.php

upon doing logcat all the conditions are true and valid , all the respective adapters are calling but items are not displaying in recyclerview

I have given the intenet permission, and added suitable dependecies of coroutines, retrofit, picasso for displaying the image

What could be the possible issue and how to resolve it, Any help will be much appreciated

This is my HomeFragment file


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

This is my Adapter for the 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)
        }
    }

}

Upvotes: 1

Views: 22

Answers (1)

Sohaib Ahmed
Sohaib Ahmed

Reputation: 3098

Modify the fetchCategory function in HomeFragment

withContext(Dispatchers.Main) {
    if (response.categories.isNotEmpty()) {
        Log.d("HomeFragment", "Category list size: ${response.categories.size}")
        
        myAdapter.catlist = response.categories
        myAdapter.notifyDataSetChanged() // Notifying the adapter about dataset change
        
        myAdapter.onItemClick = { category ->
            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()
    }
}

Update Constructor parameter's AccessModifier in adapter catAdap

class catAdap(val context: Fragment, private var catlist: List<Category>) :

Add following method in your adapter catAdap

fun updateList(newList: List<Category>) {
    catlist = newList
    notifyDataSetChanged()
}

Now, from fragment, update your list like below:

myAdapter.updateList(response.categories)

Upvotes: 0

Related Questions