user2805004 user2805004 - 2 months ago 10
Android Question

Handling OnClick on an item within a RecyclerView element?

I have a recyclerview of cardviews, and each cardview has a thumbs-up (like) button.

I would like it so that when a user clicks the thumb button on a particular card, they "like" that post, the button animates and the appropriate logic is handled.

Here is my code so far:

private class CardViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private CardView mCardView;
private TextView cardTitle, cardUsername;
private ImageView cardImage;
public CardViewHolder(View itemView){
super(itemView);
mCardView = (CardView) itemView.findViewById(R.id.cardview);
cardTitle = (TextView) itemView.findViewById(R.id.cardTitle);
cardUsername = (TextView) itemView.findViewById(R.id.cardUsername);
cardImage = (ImageView) itemView.findViewById(R.id.cardImage);
mLikeButton = (LikeButton) itemView.findViewById(R.id.thumb);

itemView.setOnClickListener(this);
mLikeButton.setOnClickListener(this);

}
@Override
public void onClick(View v) {

if (v.getId() == mLikeButton.getId()){
Toast.makeText(v.getContext(), "ITEM PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(v.getContext(), "ROW PRESSED = " + String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
Toast.makeText(v.getContext(), mRecipes[getAdapterPosition()].getRecipename().toString(), Toast.LENGTH_SHORT).show();
}
}
}


When the user clicks the like button, it does display the correct adapter position in the toast, but it does not animate like it should and I have no way of setting the animation on it. Ordinarily, the button animates when clicked, but when it's within the recyclerview it does not seem to work.

I tried to do

(LikeButton) v.setLiked(true);


but the cast didn't seem to work. I want to be able to get a direct reference to the specific like button in a specific row of the recyclerview so that I may call a method on it. Is that possible?

Is this a bad way of setting the click listener on the button?

Answer

The problem lies at the view id check. If you want to go with your solution this would be the way:

private static class CardViewHolder extends RecyclerView.ViewHolder
    implements View.OnClickListener {

private CardView mCardView;
private TextView cardTitle, cardUsername;
private ImageView cardImage;

public CardViewHolder(View itemView) {
    super(itemView);

    mCardView = (CardView) itemView.findViewById(R.id.cardview);
    cardTitle = (TextView) itemView.findViewById(R.id.cardTitle);
    cardUsername = (TextView) itemView.findViewById(R.id.cardUsername);
    cardImage = (ImageView) itemView.findViewById(R.id.cardImage);
    mLikeButton = (LikeButton) itemView.findViewById(R.id.thumb);

    itemView.setOnClickListener(this);
    mLikeButton.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    if (v instanceof LikeButton) {
        // You now this is the LikeButton
        Toast.makeText(v.getContext(), "ITEM PRESSED = " +
                String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
        ((LikeButton) v).setLiked(true);
    } else {
        // Not a LikeButton
        Toast.makeText(v.getContext(), "ROW PRESSED = " +
                String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT).show();
        Toast.makeText(v.getContext(),
                mRecipes[getAdapterPosition()].getRecipename().toString(),
                Toast.LENGTH_SHORT).show();
    }
}
}

What we do here is to check if the View is a LikeButton. If so, we can safely cast it and call our method on it. If not it is a itemView. A better way in my opinion would be, to anonymiously implement the OnClickListeners. This would look like this:

private static class CardViewHolder extends RecyclerView.ViewHolder
    implements View.OnClickListener {

private CardView mCardView;
private TextView cardTitle, cardUsername;
private ImageView cardImage;

public CardViewHolder(View itemView) {
    super(itemView);

    mCardView = (CardView) itemView.findViewById(R.id.cardview);
    cardTitle = (TextView) itemView.findViewById(R.id.cardTitle);
    cardUsername = (TextView) itemView.findViewById(R.id.cardUsername);
    cardImage = (ImageView) itemView.findViewById(R.id.cardImage);
    mLikeButton = (LikeButton) itemView.findViewById(R.id.thumb);

    itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // You now for sure this is an ItemView.
            Toast.makeText(v.getContext(), "ROW PRESSED = " +
                    String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT)
                    .show();
            Toast.makeText(v.getContext(),
                    mRecipes[getAdapterPosition()].getRecipename().toString(),
                    Toast.LENGTH_SHORT).show();
        }
    });

    mLikeButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // You now for sure this is a LikeButton.
            Toast.makeText(v.getContext(), "ITEM PRESSED = " +
                    String.valueOf(getAdapterPosition()), Toast.LENGTH_SHORT)
                    .show();
            ((LikeButton) v).setLiked(true);
        }
    });
}
}

Here we have an OnClickListener for each View we want to handle. Because every View has it's own, we always know which View has been clicked and can act accordingly.

Comments