Home > other >  How to load data in recyclerview?
How to load data in recyclerview?

Time:09-27

I am creating one Android app and trying to set the data in Recyclerview, I am using MVVM architecture pattern with kotlin, I can see data in logcat but when app loads I am not seeing any data in my recyclerview. Following is my code.

MainActivity

class MainActivity : AppCompatActivity() {
    lateinit var productViewModel: ProductViewModel
    private lateinit var binding: ActivityMainBinding

    val adapter = ProductAdapter()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        val productService = RetrofitHelper.getInstance().create(ProductService::class.java)

        val productRepository = ProductRepository(productService)
        productViewModel = ViewModelProvider(this, ProductViewModelFactory(productRepository)).get(ProductViewModel::class.java)

        binding.recyclerview.adapter = adapter
        productViewModel.products.observe(this,{
            Log.d("TEST",it.toString())
            adapter.notifyDataSetChanged()

        })
    }
}

ProductAdapter

class ProductAdapter : RecyclerView.Adapter<ProductViewHolder>() {
    var movies = mutableListOf<MobileList>()
    

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
        val inflater = LayoutInflater.from(parent.context)

        val binding = AdapterLayoutBinding.inflate(inflater, parent, false)
        return ProductViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
        val movie = movies[position]
        holder.binding.name.text = movie.products.get(position).name
        Glide.with(holder.itemView.context).load(movie.products.get(position).image_url).into(holder.binding.imageview)

    }

    override fun getItemCount(): Int {
        return movies.size
    }

}

class ProductViewHolder(val binding: AdapterLayoutBinding) : RecyclerView.ViewHolder(binding.root) {

}

Repository class

class ProductRepository (private val productService: ProductService) {

    private val productLiveData = MutableLiveData<MobileList>()
    val products:LiveData<MobileList>
    get() = productLiveData

    suspend fun getProducts(){
        val products = productService.getQuotes()
        if(products?.body()!=null)
        {
            productLiveData.postValue(products.body())
        }
    }
}

ViewModel

class ProductViewModel (private val productRepository: ProductRepository ) :ViewModel() {
    init {
        viewModelScope.launch(Dispatchers.IO){
            productRepository.getProducts()
        }
    }

    val products : LiveData<MobileList>
    get() = productRepository.products
}

Factory

class ProductViewModelFactory (private val productRepository: ProductRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return ProductViewModel (productRepository) as T
    }

}

Model

data class MobileList(
    val products: List<Product>
)

data class Product(
    val image_url: String,
    val name: String,
    val price: String,
    val rating: Int
)

JSON Response

{
  "products": [
    {
      "name": "test",
      "price": "34999",
      "image_url": "url",
      "rating": 4
    },
    {
      "name": "test2",
      "price": "999",
      "image_url": "url",
      "rating": 4
    },]}

CodePudding user response:

First of all make sure you have layoutManager set on the RecyclerView.

The problem here is Your ProductAdapter never had the data . notifyDataSetChanged is not a magic stick to notify the adapter you modify/add/update the dataset and then You will call notifyDataSetChanged . that's how it works .

In your case You have movies list your adapter but you never assigned anything to it its always empty . There are several ways to it. Just to make it work You can have a method to add the data in your adapter and then notify it.

fun addData(data:List<MobileList>){
        movies.addAll(data)
        notifyDataSetChanged()
    }

Now when you get the data inside on change you call this method .

productViewModel.products.observe(this,{
       it?.let{ items ->
        adapter.addData(items)
       }
    })

This should work .

Update on type fix - Seems like your type is messed up . Why is your repo returns a object of MobileList? While you are using a list of MobileList in adapter . Your adapter should hold var movies = mutableListOf<Products>().

productViewModel.products.observe(this,{
       it?.let{ item ->
        adapter.addData(item.products)
       }
    })
  • Related