0

I have RecyclerView adapter in Kotlin and when a user clicks on categoryPhoto, I want to open a new activity. How should I implement this?

class CategoryAdapter(private val categoryList: List<Category>, private val context: Context) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {

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

        var categoryName = view.text_view_category_name
        var categoryPhoto = view.image_view_category
        var cardView = view.card_view_category

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(parent.inflate(R.layout.category_list_row))

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

    val category = categoryList[position]

    // Set height of cardview based on screen width
    val displayMetrics = context.resources.displayMetrics
    val finalHeight = displayMetrics.widthPixels / 2
    holder.cardView.layoutParams.height = finalHeight

    holder.categoryName.text = category.oc
    holder.categoryPhoto.loadUrl(category.icon)
}

override fun getItemCount(): Int {
    return categoryList.size
}}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Mehul Kanzariya
  • 888
  • 3
  • 27
  • 58
  • Did you search? https://stackoverflow.com/questions/24471109/recyclerview-onclick – Ivan Wooll Jan 07 '18 at 11:40
  • 1
    I looked at that answer but that was in Java. I am finding it difficult to implement it in Kotlin @IvanWooll – Mehul Kanzariya Jan 07 '18 at 11:41
  • 1
    With the greatest of respect, I recommend that you take some time to learn about Java before jumping into Kotlin as the vast number of code samples/answers you will find will be in Java. This will equip you very well for your future Android career. – Ivan Wooll Jan 07 '18 at 11:46

3 Answers3

12

Just add click listener as parameter to constructor of your adapter.

class CategoryAdapter(
        private val categoryList: List<Category>, 
        private val context: Context,
        private val onClickListener: (View, Category) -> Unit
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {

    ...

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

        val category = categoryList[position]

        // Set height of cardview based on screen width
        ...

        holder.itemView.setOnClickListener { view ->
            onClickListener.invoke(view, category)
        }      
    }

   ...
}

Then you can use as following:

fun initList() {
     adapter = CategoryAdapter(
         categoryList = ...,
         context = ...,
         onClickListener = { view, category -> openActivity(view, category) }
}

Off-top. Some optional improvements for code above

  1. Create typealias for lambda. Make your code more readable.

    typealias MyCategoryClickListener = (View, Category) -> Unit
    
    class CategoryAdapter(
        private val categoryList: List<Category>, 
        private val context: Context,
        private val onClickListener: MyCategoryClickListener
    ) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
    
  2. Omit invoke call of listener. Lambda can be called just like function.

    holder.itemView.setOnClickListener { view ->
        onClickListener(view, category)
    }  
    
  3. Replace lambda with reference when creating adapter

     fun initList() {
         adapter = CategoryAdapter(
             categoryList = ...,
             context = ...,
             onClickListener = this::openActivity)
     }
    
     fun openActivity(view: View, category: Category) {
         ...
     }
    
hluhovskyi
  • 9,556
  • 5
  • 30
  • 42
  • just a small refinement to this....as per https://kotlinlang.org/docs/reference/lambdas.html, "In Kotlin, there is a convention that if the last parameter to a function is a function, and you're passing a lambda expression as the corresponding argument, you can specify it outside of parentheses" – John O'Reilly Jan 07 '18 at 12:36
  • @JohnO'Reilly I always try to follow this convention. But from my experience in case if you build some recycler adapters and pass listeners as constructor parameters it is always better to specify what it responsible for. Cause not always last lamba is click listener, it may be long or touch listener so it becomes not so clear. – hluhovskyi Jan 07 '18 at 12:55
4

You can do it in your onBindViewHolder(...)

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    val category = categoryList[position]

    // Set height of cardview based on screen width
    val displayMetrics = context.resources.displayMetrics
    val finalHeight = displayMetrics.widthPixels / 2
    holder.cardView.layoutParams.height = finalHeight

    holder.categoryName.text = category.oc
    holder.categoryPhoto.loadUrl(category.icon)

    holder.categoryPhoto.setOnClickListener { view ->

       // categoryPhoto clicked.
       // start your activity here
    }      
}
Pedro Massango
  • 4,114
  • 2
  • 28
  • 48
  • This will slow down the recycler view because `onBindViewHolder` gets called multiple times for each row – Junior Apr 05 '21 at 05:11
  • I'm not sure about that, have you tested the peroformance of this solution? Adding a callback isn't something that impact on performance! – Pedro Massango Apr 05 '21 at 07:08
  • The setOnClickListener will get called 1000s if not 100s of times depending on the number of new rows being created. Even if it doesn't slow down the recyclerview, it's a bad practice. – Junior Apr 06 '21 at 07:26
  • FYI, onBind is the place to bind data and register any callback related to the item. RecyclerView is optimied enough to avoid re-create items (AKA call the onBind). Let me know if you have any benchmark about performance or a better way to do that once you find it :) – Pedro Massango Apr 06 '21 at 16:35
2

Do like this

class RecyclerListAdapter: RecyclerView.Adapter { var context: Context? = null var listData: ArrayList? = null

Step 1: Activity ref.................................. var activityref:MainActivity?=null

    constructor(context: Context?, listData: ArrayList<ItemDetails>?, activityref: MainActivity?) : super() {
        this.context = context
        this.listData = listData
        this.activityref = activityref
    }


    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewsHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.row_list, parent, false)

        return ViewsHolder(view)
    }

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

    override fun onBindViewHolder(holder: ViewsHolder?, position: Int) {
        holder?.item=listData?.get(position)
        holder!!.first!!.setText(holder.item?.First)
        holder.second!!.setText(holder.item?.Second)
        holder.third!!.setText(holder.item?.Third)

  Step 2  OnClick on item.....................       
        holder.third!!.setOnClickListener{
            activityref?.OnItemClicked(holder.item!!)
        }

    }


    class ViewsHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {


        var item:ItemDetails?=null

        var first: TextView? = null;
        var second: TextView? = null;
        var third: TextView? = null;

        init {
            first = itemView?.findViewById(R.id.first)
            second = itemView?.findViewById(R.id.second)
            third = itemView?.findViewById(R.id.third)

        }



    }


}
Rohit269
  • 91
  • 4
  • 6