Implement view clicks on multiple ViewHolder types in RecycleView adapter
Asked Answered
S

2

0

I am implementing multiple ViewHolders as suggested in this answer which uses an abstract bind(). My current Adapter and ViewHolder looks likes:

// MyAdapter.java

{adapter code}

public static abstract class MyViewHolder extends RecyclerView.ViewHolder {

    public MyViewHolder(View itemView) {
        super(itemView);
    }

    protected abstract void bind(MyModel item);
}

// ViewHolder1.java
public class ViewHolder1 extends MyAdapter.MyViewHolder implements View.OnClickListener {

    TextView textView;

    public ViewHolder1(View itemView) {
        super(itemView);
        textView = itemView.findViewById(R.id.textView);
        textView.setOnClickListener(this);
    }

    @Override
    protected void bind(MyModel item) {
        textView.setText(item.getText());
    }

    @Override
    public void onClick(View view) {
        //pass the current item position back to adapter
    }
}

How can I pass the position of the clicked item from here back to the adapter. I don't want set onClickListener() inside bind() because it would then be called multiple times while my RecyclerView is scrolled.

Stercoricolous answered 13/9, 2019 at 14:6 Comment(5)
Have a look at this post. They're only using one View type, but it's the same thing, as far as the position.Concomitance
They use getAdapterPosition() which is not avialable to my external class. Also they don't extend an abstract ViewHolder.Stercoricolous
Your base ViewHolder class being abstract isn't really relevant. Apart from that, which external class are you talking about? The getAdapterPosition() method is a member of RecyclerView.ViewHolder, so you can call it in any ViewHolder descendant.Concomitance
So you mean MyViewHolder(View itemView, ActionListener listener) in the constructor instead of just itemView would do the work? I just wanted to make sure I am achieving this without leaks. Also, always thought getAdapterPosition() method was accessible to Adapter class only. My bad.Stercoricolous
Yeah, that'd work. I don't see any potential for leaks from that, since the listener isn't holding any references to the ViewHolders, and the Activity will outlive them, anyway.Concomitance
R
0

Sorry for Kotlin, but you should be able to translate it to Java easily.

Add a click callback interface to your ViewHolder constructor. In the constructor (the init block in Kotlin) set a View.OnClickListener on the itemView.

class ViewHolder(
        itemView: View,
        clickCallback: (position: Int) -> Unit
) : RecyclerView.ViewHolder(itemView) {

    init {
        itemView.setOnClickListener {
            clickCallback(layoutPosition)
        }
    }
}

When you create the ViewHolder, create an anonymous implementation of the click callback interface (in this case the clickCallback lambda) which provides the position of the clicked row. In this case, it uses the position to get the adapter item at the given position.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    return ViewHolder(
            itemView = LayoutInflater.from(parent.context)
                    .inflate(R.layout.action_list_item, parent, false),
            clickCallback = { position ->
                clickCallback(getItem(position))
            }
    )
}

class Adapter(private val clickCallback: (item: *YOUR_DATA_CLASS*) -> Unit)
Romantic answered 13/9, 2019 at 14:25 Comment(2)
I need onClickListener() on a internal view of recycler item (TextView in this case). You are adding the clickListener to the entire view (itemView) right?Stercoricolous
Yes, I'm adding it to the entire row. But you could add it to a child view of the row item layout as well. Or add the clicked view as an argument to the click callback method, so you can switch/if-else on which view was clicked later.Romantic
J
0
    @Override
        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
              viewHolder.btnPlus.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // btn plus 
                }
            });

            viewHolder.btnMinus.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                  // button minus
                }
            });

        }
 public class ViewHolder extends RecyclerView.ViewHolder
    {
        Button btnPlus, btnMinus;   
        public ViewHolder(@NonNull View itemView)
        {
            super(itemView);
            btnPlus = itemView.findViewById(R.id.plusbutton);
            btnMinus = itemView.findViewById(R.id.minusbtn);

        }
    }
Java answered 14/9, 2019 at 19:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.