Home > front end >  Kotlin Can't transfer data from Response to RecyclerView using retrofit
Kotlin Can't transfer data from Response to RecyclerView using retrofit

Time:10-07

I'm trying to make a currency app I can get info from the API but can't transfer the data from response to RecyclerView. When I try to change the cryptoModels to arraylist of response.body it crashes the app and giving 3 errors in RecyclerAdapter. I have tried many different variations of coding but nothing seems to work. My recyclerview doesn't get the info from API but when I try to check Response it contains the info I need.

Here is my MainActivity

    class MainActivity : AppCompatActivity(), RecyclerAdapter.Listener{

    private val BASE_URL = "https://v6.*******-api.com/**/"
    private var cryptoModels : ArrayList<CryptoModel>? = null
    private var cryptoName : CryptoModel? = null
    private var recyclerViewAdapter : RecyclerAdapter? = null
    private lateinit var binding : ActivityMainBinding


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // RecyclerView

        val layoutManager : RecyclerView.LayoutManager = LinearLayoutManager(this)
        binding.recyclerView.layoutManager = layoutManager
        loadData()

    }

    private fun loadData() {
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        val service = retrofit.create(CryptoAPI::class.java)
        val call = service.getData()


        call.enqueue (object : Callback<CryptoModel> {

            override fun onFailure(call: Call<CryptoModel>, t: Throwable) {
                t.printStackTrace()
            }

            override fun onResponse(
                call: Call<CryptoModel>,
                response: Response<CryptoModel>,

            ) {
                if (response.isSuccessful) {

                    response.body()?.let {
                      cryptoModels.let {
                          if(it!= null) {
                              recyclerViewAdapter = RecyclerAdapter(it!!, this@MainActivity)
                              binding.recyclerView.adapter = recyclerViewAdapter
                          }

                      }
                    }
                }
            }
        })
    }

    override fun onItemClick(cryptoModel: CryptoModel) {
        Toast.makeText(this,"Clicked :   ${cryptoModel.conversion_rates}",Toast.LENGTH_LONG).show()
    }

}

here is my RecyclerAdapter

 class RecyclerAdapter(private val cryptoList : ArrayList<CryptoModel>, private val listener 
 : Listener) : RecyclerView.Adapter<RecyclerAdapter.RowHolder>() {

    private val colors : Array<String> = 
    arrayOf("#FFFFFF","#FF0000","#00FF00","#0000FF","#008000","#00FFFF")

    interface Listener {
        fun onItemClick(cryptoModel: CryptoModel)
    }

    class RowHolder(view : View) : RecyclerView.ViewHolder(view) {

        private lateinit var binding : RecyclerRowMainBinding

        fun bind(cryptoModel : CryptoModel, colors : Array<String>, position: Int, listener : 
    Listener) {
            itemView.setOnClickListener {
                listener.onItemClick(cryptoModel)
            }
            itemView.setBackgroundColor(Color.parseColor(colors[position %6]))
            binding.currencyName.text = cryptoModel.base_code
            binding.currencyPrice.text = cryptoModel.conversion_rates.toString()
        }

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowHolder {
        val view = 
    LayoutInflater.from(parent.context).inflate(R.layout.recycler_row_main,parent,false)
        return RowHolder(view)
    }

    override fun onBindViewHolder(holder: RowHolder, position: Int) {
        holder.bind(cryptoList[position],colors,position,listener)

    }

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

and my CryptoModel

 data class CryptoModel(
   
    val conversion_rates : Map<String,Double> ,
    val base_code : String ,)

and my CryptoAPI

interface CryptoAPI {

    @GET("ea37f798********adf1/latest/USD")
    fun getData()  : Call<CryptoModel>



}

Recycler Row xml codes

    <LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@ id/currency_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="26sp"
        android:layout_margin="10dp"
        android:textColor="@color/white"
        >
    </TextView>

    <TextView
        android:id="@ id/currency_price"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_margin="2dp"
        android:textColor="@color/white"
        >
    </TextView>

</LinearLayout>

and lastly my recyclerview xml codes

<androidx.constraintlayout.widget.ConstraintLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@ id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="1dp"
        android:layout_marginEnd="1dp"
        android:layout_marginBottom="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

My API looks like this

CodePudding user response:

Your ViewHolder (RawHolder) is not initializing the binding variable. You need to inflate your layout using DataBindingUtil in order to have a binding reference:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowHolder {
    val binding = DataBindingUtil.inflate(
        LayoutInflater.from(parent.context),
        R.layout.recycler_row_main,
        parent,
        false)
    return RawHolder(binding)
}

Then, pass it to your RawHolder class.

class RowHolder(
    binding: RecyclerRowMainBinding
) : RecyclerView.ViewHolder(binding.root) {

    fun bind(
        cryptoModel : CryptoModel, 
        colors : Array<String>, 
        position: Int, 
        listener : Listener) {
            itemView.setOnClickListener {
                listener.onItemClick(cryptoModel)
            }
            itemView.setBackgroundColor(Color.parseColor(colors[position %6]))
            binding.currencyName.text = cryptoModel.base_code
            binding.currencyPrice.text = cryptoModel.conversion_rates.toString()
        }
    }

This code is just to give an idea about the solution, adjust it to your needs..

CodePudding user response:

So the code works after some changes first of all binding in the RecyclerAdapter doesn't work so I've changed it like this

    package com.example.retrofitcrypto.adapter

import android.graphics.Color

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.retrofitcrypto.R
import com.example.retrofitcrypto.model.CryptoConverterModel



class RecyclerAdapter(private val cryptoList : ArrayList<CryptoConverterModel>, private val listener : Listener) : RecyclerView.Adapter<RecyclerAdapter.RowHolder>() {



    private val colors : Array<String> = arrayOf("#FFFFFF","#FF0000","#00FF00","#0000FF","#008000","#00FFFF")

    interface Listener {
        fun onItemClick(cryptoConverterModel: CryptoConverterModel)
    }

     class RowHolder(view : View) : RecyclerView.ViewHolder(view) {

         val currencyName : TextView = view.findViewById(R.id.currencyName)
         val currencyPrice : TextView = view.findViewById(R.id.currencyPrice)



        fun bind(cryptoConverterModel: CryptoConverterModel, colors : Array<String>, position: Int, listener : Listener) {

            itemView.setOnClickListener {
                listener.onItemClick(cryptoConverterModel)
            }
            itemView.setBackgroundColor(Color.parseColor(colors[position %6]))
            currencyName.setText(cryptoConverterModel.cryptoName)
            currencyPrice.setText(cryptoConverterModel.cryptoRate.toString())


        }



     }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowHolder {

        val view = LayoutInflater.from(parent.context).inflate(R.layout.recycler_row_main,parent,false)
        return RowHolder(view)
    }



    override fun onBindViewHolder(holder: RowHolder, position: Int) {

        holder.bind(cryptoList[position],colors,position,listener)

    }

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



}

Also I've added a new class to break the conversiton_rates Map<String, Double> into two seperate pieces

   class CryptoConverterModel {
    var cryptoName : String? = null
    var cryptoRate : Double? = null
}

And finally I've changed MainActivity codes like this

   package com.example.retrofitcrypto.view

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.retrofitcrypto.adapter.RecyclerAdapter
import com.example.retrofitcrypto.databinding.ActivityMainBinding
import com.example.retrofitcrypto.model.CryptoConverterModel
import com.example.retrofitcrypto.model.CryptoModel
import com.example.retrofitcrypto.service.CryptoAPI
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory


class MainActivity : AppCompatActivity(), RecyclerAdapter.Listener{

    private val BASE_URL = "https://v6.exchangerate-api.com/v6/"
    private var recyclerViewAdapter : RecyclerAdapter? = null
    private lateinit var binding : ActivityMainBinding


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // RecyclerView

        val layoutManager : RecyclerView.LayoutManager = LinearLayoutManager(this)
        binding.recyclerView.layoutManager = layoutManager

        loadData()
    }

    private fun loadData() {
        val retrofit = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()

        val service = retrofit.create(CryptoAPI::class.java)
        val call = service.getData()


        call.enqueue (object : Callback<CryptoModel> {

            override fun onFailure(call: Call<CryptoModel>, t: Throwable) {
                t.printStackTrace()
            }

            override fun onResponse(
                call: Call<CryptoModel>,
                response: Response<CryptoModel>
            ) {
                if (response.isSuccessful) {
                    val cryptoArrayRates = ArrayList<CryptoConverterModel>()
                    response.body().let {
                        it?.conversion_rates?.forEach{
                            var crptoConverterModel = CryptoConverterModel()
                            crptoConverterModel.cryptoName = it.key
                            crptoConverterModel.cryptoRate = it.value
                            cryptoArrayRates.add(crptoConverterModel)

                        }
                        recyclerViewAdapter = RecyclerAdapter(cryptoArrayRates,this@MainActivity)
                        binding.recyclerView.adapter = recyclerViewAdapter
                    }



                    }
                }

        })
    }

    override fun onItemClick(crptoConverterModel: CryptoConverterModel) {
        Toast.makeText(this,"Clicked :   ${crptoConverterModel.cryptoName}",Toast.LENGTH_LONG).show()
    }

}

The App works fine now thanks for all the help gioravered

  • Related