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