What is the right approach to get the click / position in RecyclerView?

8

What is the right approach to get the click on RecyclerView?

1 - Within the onBindViewHolder use the position, even within the setOnClickListener methods, which turns the variable position into FINAL; 2 - Within the onBindViewHolder use the holder.getAdapterPosition (), even within the setOnClickListener methods, which turns the holder variable into FINAL; 3 - Some other way that involves interface / other ways, like in this examples . Other.

EXAMPLE:

@Override
    public void onBindViewHolder(final AdapterViewHolder holder, final int position) {


        holder.imagem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "posicao "+position,Toast.LENGTH_SHORT).show();
                Toast.makeText(context, "posicao "+holder.getAdapterPosition(),Toast.LENGTH_SHORT).show();
            }
        });


    }
    
asked by anonymous 19.10.2016 / 15:22

2 answers

9

It's always complicated to say what the "right approach" is, it can be better in one case and worse in others.

However, the onClickListener is assigned to view . Who is responsible for giving view to adapter is ViewHolder class. I think that should be where they should be attributed.

On the other hand, assigning "listeners" to onBindViewHolder() will cause them to be assigned whenever a line appears in RecyclerView. By assigning them in the ViewHolder constructor they will be reused, being assigned only once.

To get the position (1) use the getAdapterPosition () .

public static class MyViewHolder extends RecyclerView.ViewHolder 
                                 implements View.OnClickListener{

    private final ImageView imageView;

    public MyViewHolder(View v) {
        super(v);

        imageView = (ImageView) v.findViewById(R.id.imageView);

        //Atribui o listener ao layout da linha.
        v.setOnClickListener(this);
        // no entanto ele pode ser aplicado a qualquer uma das views dele.
        //imageView.setOnClickListener(this);
    }

    //Implementa View.OnClickListener
    @Override
    public void onClick(View v) {
        Log.d(TAG, "Elemento " + getAdapterPosition() + " clicado.");
    }
}

The onCreateViewHolder() of adapter would look like this:

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

    View v = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.row_item, viewGroup, false);

    return new MyViewHolder(v);
}

The onBindViewHolder() method should only assign the values to views .

This approach can be improved so that the code to be executed when an item is clicked is external to the adapter .

Define an interface to be implemented by the class that will execute the code when an item is clicked:

public interface ItemClickListener {

    void onItemClick(int position);
}

Add an attribute and its setter to save an instance that implements this interface:

private static ItemClickListener itemClickListener;

public void setOnItemClickListener(ItemClickListener itemClickListener){
    this.itemClickListener = itemClickListener;
}

Change the ViewHolder to call the onItemClick() method of this instance instead of implementing the code that handles the click.

public class MyViewHolder extends RecyclerView.ViewHolder 
                                 implements View.OnClickListener{

    private final ImageView imageView;

    public MyViewHolder(View v) {
        super(v);

        imageView = (ImageView) v.findViewById(R.id.imageView);

        //Atribui o listener ao layout da linha.
        v.setOnClickListener(this);
        // no entanto ele pode ser aplicado a qualquer uma das views dele.
        //imageView.setOnClickListener(this);
    }

    //Implementa View.OnClickListener
    @Override
    public void onClick(View v) {

        if(itemClickListener != null) {
            itemClickListener.onItemClick(getAdapterPosition());
        }
    }
}

Now it is possible to declare the code to execute when an item is clicked, where the adapter is instantiated

adapter.setOnItemClickListener(new ItemClickListener() {
    @Override
    public void onItemClick(int position) {
        Log.d(TAG, "Elemento " + position + " clicado.");
    }
});

(1) - Justified by this excerpt from documentation :

  

(...) Sometimes, you may need to get the exact adapter position of some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.

     

(...) Sometimes it may be necessary to get the exact position of the adapter to do some actions in response to user events. In this case, you should use this method that will calculate the position of the ViewHolder on the adapter.

    
19.10.2016 / 18:35
2

I usually set a TAG to get the correct position.

For example:

@Override
    public void onBindViewHolder(final AdapterViewHolder holder, final int position) {

        holder.imagem.setTag(position);
        holder.imagem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int clickPosition = (int) view.getTag();

                Toast.makeText(context, "posicao "+clickPosition,Toast.LENGTH_SHORT).show();

            }
        });


    }
    
19.10.2016 / 15:30