Showing an image with recyclerview and retrofit - android

I have a problem in my app where I need to display an image with recyclerview but I'm getting a null object.
class AllSubscribersAdapter(val context: Context, private val subsribedChannels: SubscribedChannels): RecyclerView.Adapter<AllSubscribersAdapter.ViewHolder>() {
private var channels: List<Channels> = listOf()
init {
channels = subsribedChannels.channels
}
override fun onCreateViewHolder(parent: ViewGroup, position: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.subscribers_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return channels.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.tvChannelName.text = channels[position].name
Glide.with(holder.channel_img)
.load(channels[position].avatar)
.transform(CircleCrop())
.into(holder.channel_img)
/*Picasso.get()
.load(channels[position].avatar)
.fit()
.into(holder.channel_img)*/
}
fun setChannelsList(channels: List<Channels>){
this.channels = channels
notifyDataSetChanged()
}
class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) {
var tvChannelName: TextView = itemView!!.findViewById(R.id.channel_name)
var channel_img: ImageView = itemView!!.findViewById(R.id.channel_image)
}
}
channel_name is working but channel_image is null, any help is appreciated. Here is my model class.
class SubscribedChannels (
var channels: List<Channels>
): Serializable
data class Channels(
var id: String,
var name: String,
var slug: String,
var avatar: String,
var created_by: String,
var subscribers: String
): Serializable
I don't know if there is a problem here.

First thing you cannot initialize variable in ViewHolder class.
You should do it in constructor just like this.
var tvChannelName: TextView?=null
var channel_img: ImageView?=null
init{
tvChannelName = itemView!!.findViewById(R.id.channel_name)
channel_img = itemView!!.findViewById(R.id.channel_image)
}
And if you are getting empty string in your retrofit response then you have check string then you have to call glide.

At the bottom holder.tvChannelName.text you can log chanels. Avatar if is not empty you can check the value have the valid image url

Related

error in using Gson Api with recyclerview?

hello guys I am using a gson api with a Recyclerview and I got this error with the adapter and it says that the
expression 'myAdapter' of type 'myAdapter' cannot be invoked as a function. the function 'invoked' is not found
and when I run the code without the data it gives me this error:
lateinit property myAdapter has not been initialized
my activities and classes
private val datalist: MutableList<Sura> = mutableListOf()
private lateinit var myAdapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quran_a_p_i)
myAdapter = myAdapter(datalist) // here is the error
apiQuranRecyclerView.layoutManager = LinearLayoutManager(this)
apiQuranRecyclerView.addItemDecoration(DividerItemDecoration(this, OrientationHelper.VERTICAL))
apiQuranRecyclerView.adapter = myAdapter
AndroidNetworking.initialize(this)
AndroidNetworking.get("http://api.islamhouse.com/v1/mykey/quran/get-category/462298/ar/json/")
.build()
.getAsObject(Surasnames::class.java, object : ParsedRequestListener<Surasnames>{
override fun onResponse(response: Surasnames) {
datalist.addAll(response.suras)
myAdapter.notifyDataSetChanged()
}
override fun onError(anError: ANError?) {
}
})
}
my Adapter
class MyAdapter (private val datalist: MutableList<Sura>): RecyclerView.Adapter<myHolder>() {
private lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): myHolder {
context = parent.context
return myHolder(LayoutInflater.from(context).inflate(R.layout.api_quran_view, parent, false))
}
override fun getItemCount(): Int = datalist.size
override fun onBindViewHolder(holder: myHolder, position: Int) {
val data = datalist[position]
val apisuraname = holder.itemView.apiSuraNames
val surasnames = "${data.id} ${data.title}"
apisuraname.text = surasnames
holder.itemView.setOnClickListener {
Toast.makeText(context, surasnames, Toast.LENGTH_SHORT).show()
}
}
my Holder
class myHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
my Models
data class Sura(
#SerializedName("add_date")
val addDate: Int,
#SerializedName("api_url")
val apiUrl: String,
#SerializedName("id")
val id: Int,
#SerializedName("title")
val title: String,
#SerializedName("type")
val type: String
)
the second one
data class Surasnames(
#SerializedName("add_date")
val addDate: Int,
#SerializedName("description")
val description: String,
#SerializedName("id")
val id: Int,
#SerializedName("locales")
val locales: List<Any>,
#SerializedName("suras")
val suras: List<Sura>,
#SerializedName("title")
val title: String)
thanks in advance
You need to call the constructor. Change the line
myAdapter = myAdapter(datalist)
to
myAdapter = MyAdapter(datalist)

Having inner classes and interfaces in Data class of Kotlin is best practice or not

I have a data class which is a room entity and the code is below
#Entity(tableName = "request")
data class Request(
#PrimaryKey
var id: Int,
var name: String,
var date: String,
var requestTime: String,
var deliveryTime: String,
var distanceCovered: String
){
interface OnClickListener{
fun onClick(request: Request)
}
class Adapter(
private val requests: List<Request>,
private val requestsFor: Int,
private val listener: Request.OnClickListener
) : RecyclerView.Adapter<Adapter.ViewHolder>() {
companion object{
const val REQUESTS_FOR_SEND = 0
const val REQUESTS_FOR_ONGOING = 1
const val REQUESTS_FOR_COMPLETED = 2
}
class ViewHolder(
var recyclerViewRequestBinding: RequestBinding
) : RecyclerView.ViewHolder(recyclerViewRequestBinding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.request,
parent,
false
)
)
override fun getItemCount() = requests.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val request = requests[position]
holder.recyclerViewRequestBinding.request = request
when(requestsFor){
REQUESTS_FOR_COMPLETED ->{
holder.itemView.request_date.hide()
holder.itemView.request_delivery_time.show()
holder.itemView.request_distance_covered.show()
}
}
holder.itemView.setOnClickListener { listener.onClick(requests[position]) }
}
}
}
Here you can see that Request is an Room entity and it has its inner class Adapter and interface OnClickListener having these thing inside the entity is a good practice or not if it is a bad practice then what is the best practice?
According to the SOLID rules, Every class should have single responsibility. You can make ViewHolder inner class of Your Adapter. Interfaces and Entities should be separated.

Can't populate a recycler using a collection

I want each row of my RecyclerView to display all the details of one document of the collection.
I've used this exact same adapter code, albeit with a different class to serialize into. And it works well. But in this instance, it's simply not working.
But the code just doesn't get into populating the views.
My database is like:
reviews--Orange--vault--|
|-firstReview
|-secondReview
|-sjdeifhaih5aseoi
...
My query and adapter from the fragment:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(ReviewViewModel::class.java)
val reviewQuery = FirebaseFirestore.getInstance().collection("reviews").document("Orange").collection("vault")
val reviewBurnOptions = FirestoreRecyclerOptions.Builder<Review>()
.setQuery(reviewQuery, object : SnapshotParser<Review> {
override fun parseSnapshot(snapshot: DocumentSnapshot): Review {
return snapshot.toObject(Review::class.java)!!.also {
it.id = snapshot.id
}
}
}).setLifecycleOwner(this)
reviewRecycler.adapter=ReviewBurnAdapter(reviewBurnOptions.build())}
class ReviewBurnAdapter(options: FirestoreRecyclerOptions<Review>) :
FirestoreRecyclerAdapter<Review, ReviewBurnAdapter.ViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {//I never reach this point
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_review, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, item: Review) {
holder.apply {
holder.itemView.rowAuthor.text = item.author
}
}
inner class ViewHolder(override val containerView: View) :
RecyclerView.ViewHolder(containerView), LayoutContainer
}
Class to serialize into:
import com.google.firebase.firestore.Exclude
import com.google.firebase.firestore.PropertyName
import java.util.*
class Review(
#get:Exclude var id: String = "DEVIL",
#JvmField #PropertyName(AUTHOR) var author: String = "",
#JvmField #PropertyName(WRITEUP) var writeup: String = "",
//#JvmField #PropertyName(MOMENT) var moment:Date=Date(1997,12,1),
#JvmField #PropertyName(RATING) var rating: Int = 0
) {
companion object {
const val AUTHOR = "author"
const val WRITEUP = "writeup"
const val RATING = "rating"
//const val MOMENT="moment"
}
}
Also, there's no errors, it just never reaches the code that would generate and populate with viewHolders.
Alright, the fix was ultra simple, as #Prashant Jha pointed out, I hadn't specified a layout manager for my RecyclerView -_-
To be crystal clear, I added app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
to my xml, and everything worked.

Dynamic RecyclerView Adapter accepting any list

I have an app in which depending on the currency selected, I pass the list to the adapter and based on the type of list passed as an argument, I decide which model class should be used.
RecyclerView Adapter
class CoinAdapter : RecyclerView.Adapter<CoinAdapter.MyViewHolder> {
private var coinList: List<Coin>? = null
private var coinINRList: List<CoinINR>? = null
private var coinEURList: List<CoinEUR>? = null
private var coinGBPList: List<CoinGBP>? = null
private var context: Context? = null
inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var coinName: TextView
var coinPrice: TextView
init {
coinName = view.findViewById(R.id.coin_title_text)
coinPrice = view.findViewById(R.id.coin_price_text)
}
}
constructor(coinList: List<Coin>?, context: Context?) {
this.coinList = coinList
this.context = context
}
constructor(coinList: List<CoinINR>?, context: Context?, second: String) {
this.coinINRList = coinList
this.context = context
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.coin_list_row, parent, false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
when (currencyUnit) {
"USD" -> {
val coin = coinList?.get(position)
holder.coinName.text = coin?.name
holder.coinPrice.text = coin?.price
}
"INR" -> {
val coinINR = coinINRList?.get(position)
holder.coinName.text = coinINR?.name
holder.coinPrice.text = coinINR?.price
}
}
}
override fun getItemCount(): Int {
when (currencyUnit) {
"USD" -> return coinList?.size ?: 0
"INR" -> return coinINRList?.size ?: 0
else -> return coinList?.size ?: 0
}
}
}
Now, I need to support multiple currencies and so the code is becoming boilerplate. Is there any way that I can make the RecyclerView accept any type of list and then perform task depending on the list?
Thanks in advance.
My suggestion is to create a class Coin that will be a parent of all other currency objects.
open class Coin(val name: String, val price: Float)
data class CoinINR(name: String, price: Float) : Coin(name, price)
Than your adapter would have only one List and your onBindViewHolder method will look like this:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
with (coinList?.get(position)) {
holder.coinName.text = it.name
holder.coinPrice.text = it.price
}
}

recyclerview onClick getkey from firebase

How do I get key in kotlin in RecyclerView to send it to another activity?
I tried using getKey() and position but it doesn't work.
Here is my code:
class RecyclerAdapter (val userlist:ArrayList<RecyClass>,ccc: Context): RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
var mcontext =ccc
val Postdata = java.util.ArrayList<RecyClass>()
lateinit var ref:DatabaseReference
override fun getItemCount(): Int {
return userlist.size
}
override fun onBindViewHolder( holder: ViewHolder?, position: Int) {
val user: RecyClass = userlist[position]
ref = FirebaseDatabase.getInstance().getReference(user.toString())
holder?.txtviewdesc?.text = user.desc
Picasso.with(mcontext).load(user.image).into(holder?.imageviewx)
holder?.setOncustomcilcklistner(object :Custumclicklistner33{
override fun oncustomOnClickListner(view: View, pos: Int) {
}
})
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
this.Postdata
this.mcon
val v = LayoutInflater.from(parent?.context).inflate(R.layout.layoutrcview,parent,false)
return ViewHolder(v,mcon,userlist)
}
class ViewHolder(itemview: View,ccc2:Context,userlist: ArrayList<RecyClass>): RecyclerView.ViewHolder(itemview), View.OnClickListener{
var mCustIemClick5: Custumclicklistner33?=null
var txtviewdesc: TextView
var mcon =ccc2
var userdata =userlist
var imageviewx: ImageView
init {
this.userdata
this.mcon
txtviewdesc = itemview.findViewById<TextView>(R.id.editTextdesc)
imageviewx = itemview.findViewById<ImageView>(R.id.imgvw)
itemview.setOnClickListener(this)
}
fun setOncustomcilcklistner(customclick5: Custumclicklistner33){
this.mCustIemClick5=customclick5
}
override fun onClick(view: View?) {
this.mCustIemClick5!!.oncustomOnClickListner(view!!,adapterPosition)
val pos = adapterPosition
val ref =FirebaseDatabase.getInstance().getReference(pos.toString()).key.toString()
Toast.makeText(mcon,ref,Toast.LENGTH_LONG).show()
var postDetail = this.userdata[pos]
val kkk = Intent(this.mcon,ProfileActivity::class.java)
kkk.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
this.mcon.startActivity(kkk)
}
}
}
fun onclick takes to next activity but it takes post's key. How to get it?
getref and postiton is not working in fun onclick but it works on bindviewholder.
You need to use PostData[position] to get the object that current position and then a key/field from that object that you can reference.

Categories

Resources