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.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
Andro
  • 952
  • 9
  • 19
  • Have a look at [this post](https://stackoverflow.com/q/40584424). They're only using one `View` type, but it's the same thing, as far as the position. – Mike M. Sep 13 '19 at 21:54
  • They use getAdapterPosition() which is not avialable to my external class. Also they don't extend an abstract ViewHolder. – Andro Sep 14 '19 at 08:57
  • 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. – Mike M. Sep 14 '19 at 09:00
  • 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. – Andro Sep 14 '19 at 09:17
  • Yeah, that'd work. I don't see any potential for leaks from that, since the listener isn't holding any references to the `ViewHolder`s, and the `Activity` will outlive them, anyway. – Mike M. Sep 14 '19 at 09:30

2 Answers2

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)
Isak
  • 116
  • 9
  • 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? – Andro Sep 13 '19 at 14:36
  • 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. – Isak Sep 16 '19 at 13:31
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);

        }
    }