Yellow Clock
Yellow Clock

Reputation: 465

Kotlin Recycleview ,how to make onItemClick for views

I am attempting to make a function : In recyclerView when you click the user image ,navigate to the userActivity, and when you click the "gift" icon ,navigate to otherActivity .

enter image description here

As follow is my Adapter.kt :

package Users.UserReceiveGiftItem

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gearsrun.www.R
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.user_receive_gift_item.view.*

class UserReceiveGiftAdapter(val userList : List<UserReceiveGiftItem>) : RecyclerView.Adapter<UserReceiveGiftAdapter.UserHolder>(){
    private lateinit var mListener :onItemClickListener
   interface onItemClickListener{
       fun onItemClick(view:View,position: Int)
   }

    fun setOnItemClickListener(listener: onItemClickListener){
        mListener = listener
    }


    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): UserHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
        return UserHolder(layoutInflater.inflate(R.layout.user_receive_gift_item,parent,false),mListener)
    }

    override fun onBindViewHolder(holder: UserReceiveGiftAdapter.UserHolder, position: Int) {
         holder.render(userList[position])
    }


    override fun getItemCount(): Int = userList.size


    class UserHolder(val view : View,listener:onItemClickListener) : RecyclerView.ViewHolder(view){
       fun render(userList: UserReceiveGiftItem){
           Picasso.get().load(userList.user_img).into(view.user_img)
           view.user_name.text = userList.user_name
           view.time.text = userList.time
           view.userId.text = userList.userId
           view.giftImg.setImageResource(userList.giftImg)


       }
        init {
            view.setOnClickListener {
                listener.onItemClick(it,absoluteAdapterPosition)
            }

        }
    }
}

And I use in the Activity :

package com.gearsrun.www.UI.Receive

import Users.UserReceiveGiftItem.UserReceiveGiftAdapter
import Users.UserReceiveGiftItem.UserReceiveGiftItem
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.ActionBar
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.gearsrun.www.R
import com.gearsrun.www.UI.Gift.AwaitingToUnwrapActivity
import com.gearsrun.www.UI.Sunflower.SunflowerAvailableActivity
import com.gearsrun.www.UI.User.UserDetailActivity
import kotlinx.android.synthetic.main.activity_who_receive_gift.*
import kotlinx.android.synthetic.main.user_receive_gift_item.view.*

class ReceiveActivity : AppCompatActivity() {
    val userList : List<UserReceiveGiftItem> = listOf(
        UserReceiveGiftItem(
            "https://i.pinimg.com/564x/63/85/68/63856877880614e0dab080071513156f.jpg",
            "Sharry",
            "10 mins ago",
            "517ddY",
            R.drawable.donut
        ),
     
    )
    lateinit var gift_unwrap : ImageView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_receive)
        changeColor(R.color.font_green)
        initRecycler()
        gift_unwrap = findViewById(R.id.gift_unwrap)
        gift_unwrap.setOnClickListener {


        }
    }
    private fun changeColor(resourseColor: Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.statusBarColor = ContextCompat.getColor(applicationContext, resourseColor)
        }

        val bar: ActionBar? = supportActionBar
        if (bar != null) {
            bar.setBackgroundDrawable(ColorDrawable(resources.getColor(resourseColor)))
        }

    }

    fun initRecycler(){
        rvUser.layoutManager = LinearLayoutManager(this)
        val adapter = UserReceiveGiftAdapter(userList)
        rvUser.adapter = adapter
        adapter.setOnItemClickListener(object:UserReceiveGiftAdapter.onItemClickListener{
            override fun onItemClick(view: View, position: Int) {
               view.user_img.setOnClickListener {
                   val intent = Intent(this@ReceiveActivity,UserDetailActivity::class.java)
                   startActivity(intent)
               }
                view.giftImg.setOnClickListener {
                    val intent =Intent(this@ReceiveActivity,SunflowerAvailableActivity::class.java)
                    startActivity(intent)
                }
            }


        })

    }
}

It is a bit rare that it is able to navigate successfully ,however ,it dosen't react for the first click ..Could you please take a look my code ?Thank you guys in advance !!

Upvotes: 0

Views: 168

Answers (2)

Wajid
Wajid

Reputation: 2341

What I think after looking your code is, that you are setting adapter to your recyclerview first and then you are injecting a click listener to adapter.

What happens here is that some items from recycler view are loaded and they wouldn't have the click listener at that time,

for example if you scroll down, the newly appeared items on the screen will have proper click listeners, now if you scroll up, the previous one's will also have listener attached.

What to do to get rid of this problem:

Change your initRecycler() method as below:

fun initRecycler(){
        rvUser.layoutManager = LinearLayoutManager(this)
        val adapter = UserReceiveGiftAdapter(userList)
        adapter.setOnItemClickListener(object:UserReceiveGiftAdapter.onItemClickListener{
            override fun onItemClick(view: View, position: Int) {
               view.user_img.setOnClickListener {
                   val intent = Intent(this@ReceiveActivity,UserDetailActivity::class.java)
                   startActivity(intent)
               }
                view.giftImg.setOnClickListener {
                    val intent =Intent(this@ReceiveActivity,SunflowerAvailableActivity::class.java)
                    startActivity(intent)
                }
            }


        })

        //set adapter after setting click listener to your adapter.
        rvUser.adapter = adapter

    }

Upvotes: 1

Tenfour04
Tenfour04

Reputation: 93551

You are using the click listener of the list item to add more click listeners to its children. So the first time you click it, the children don't have listeners yet and won't do anything. It is also error prone that a parent and its children both have click listeners, because it's undefined what will happen if you change the click listener as you are clicking it.

Instead, you should define your interface in the adapter to handle both types of clicks. In the ViewHolder, set click listeners only on the relevant children to be clicked.

Also, I think you're misusing lateinit. I would make the property nullable. And it is redundant to have a setter function for a property's value.

If you make the ViewHolder class inner, then you don't have to pass instances of the listener to the view holder instances, which will cause problems if you ever change the Adapter's listener.

Finally, personally I think the listener should return the list item, not the specific view and position in the list. Those are implementation details that the outer class doesn't need to know about.

class UserReceiveGiftAdapter(val userList : List<UserReceiveGiftItem>) : RecyclerView.Adapter<UserReceiveGiftAdapter.UserHolder>(){
    var onItemClickListener: OnItemClickListener? = null

    interface OnItemClickListener{
        fun onUserClick(item: UserReceiveGiftItem)
        fun onGiftClick(item: UserReceiveGiftItem)
    }

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

    override fun onBindViewHolder(holder: UserReceiveGiftAdapter.UserHolder, position: Int) {
         holder.render(userList[position])
    }

    override fun getItemCount(): Int = userList.size

    inner class UserHolder(val view : View) : RecyclerView.ViewHolder(view){
        fun render(userList: UserReceiveGiftItem){
            Picasso.get().load(userList.user_img).into(view.user_img)
            view.user_name.text = userList.user_name
            view.time.text = userList.time
            view.userId.text = userList.userId
            view.giftImg.setImageResource(userList.giftImg)
        }

        init {
            view.user_img.setOnClickListener {
                onItemClickListener?.onUserClick(userList[absoluteAdapterPosition])
            }
            view.giftImg.setOnClickListener {
                onItemClickListener?.onGiftClick(userList[absoluteAdapterPosition])
            }
        }
    }
}

In Activity or Fragment:

adapter.onItemClickListener = object: UserReceiveGiftAdapter.OnItemClickListener {
    override fun onUserClick(item: UserReceiveGiftItem) {
        val intent = Intent(this@ReceiveActivity, UserDetailActivity::class.java)
        startActivity(intent)
    }

    override fun onGiftClick(item: UserReceiveGiftItem) {
        val intent = Intent(this@ReceiveActivity, SunflowerAvailableActivity::class.java)
        startActivity(intent)
    }
}

Upvotes: 1

Related Questions