Problem with recycler-adapter onClick item

1

I have a problem with onClick method in my recycler adapter.

I feed my adapter from fragment and then in adapter i get all the information i need from firebase including users info and buttons "join" that were already clicked (i set red background to that button). Everything works fine untill i click on button "join" that was not pressed before. The color of the button changes (works fine and saves info to firebase with joinEvent method) but sometimes the color of the nearby buttons changes as well. I did research about the issue but nothing works for me. I tried assigning position to the methods (joinEvent, checkIfInterested, checkIfAccepted) instead of holders but that does not work. I would appreciate any help! This is my adapter:

public class SuggestionHomeAdapter extends RecyclerView.Adapter<SuggestionHomeAdapter.ViewHolder> {
private Context mContext;
private List<Suggestion> mSuggestion;
private FirebaseUser fuser;
int index = -1;


public SuggestionHomeAdapter(Context mContext, List<Suggestion> mSuggestion) {
    this.mSuggestion = mSuggestion;
    this.mContext = mContext;
}

@NonNull
@Override
public SuggestionHomeAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(mContext).inflate(R.layout.suggestion_home_item, parent, false);
    return new SuggestionHomeAdapter.ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull final SuggestionHomeAdapter.ViewHolder holder, final int position) {

    final Suggestion suggestion = mSuggestion.get(position);
    fuser = FirebaseAuth.getInstance().getCurrentUser();


    holder.title.setText(suggestion.getTitle());
    holder.description.setText(suggestion.getDescription());
    holder.join.setBackgroundColor(0xF0DC82);
    getUserInfo(holder.username, holder.profile_image, suggestion.getSpublisher());

    checkIfInterested(holder.join, suggestion.getSid(), fuser.getUid());
    checkIfAccepted(holder.join, suggestion.getSid(), fuser.getUid());

    ColorDrawable buttonColor = (ColorDrawable) holder.join.getBackground();
    final int colorId = buttonColor.getColor();

    holder.join.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(colorId != 0xFFFF0000 && colorId != 0xff00ff00){
                joinEvent(fuser.getUid(), suggestion.getSid(), suggestion.getSpublisher());
            notifyItemChanged(position);}
        }
    });
}

@Override
public int getItemCount() {
    return mSuggestion.size();
}

public class ViewHolder extends RecyclerView.ViewHolder {

    public TextView username;
    public ImageView profile_image;
    public TextView title;
    public TextView description;
    public Button join;

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

        username = itemView.findViewById(R.id.username);
        profile_image = itemView.findViewById(R.id.profile_image);
        title = itemView.findViewById(R.id.title);
        description = itemView.findViewById(R.id.description);
        join = itemView.findViewById(R.id.btn_join);

    }
}

private void getUserInfo(final TextView username, final ImageView profile_image, final String sPublisher) {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users").child(sPublisher);
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            final User user = dataSnapshot.getValue(User.class);
            username.setText(user.getUsername());

            if (user.getImageURL().equals("default")) {
                profile_image.setImageResource(R.mipmap.ic_launcher);
            } else {
                Glide.with(mContext.getApplicationContext()).load(user.getImageURL()).into(profile_image);
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

}

this is my joinEvent method in it

private void joinEvent(String fuser, String sId, String publisher){
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");

    String iId = reference.push().getKey();

    HashMap<String, Object> map = new HashMap<>();
    map.put("interested", fuser);
    map.put("sid", sId);
    map.put("iId", iId);
    map.put("publisher", publisher);
    map.put("accepted", "");
    reference.child(iId).setValue(map);

    //join.setBackgroundColor(0xFFFF0000);

}

and my checkIfInterested and checkIfAccepted methods in it

private void checkIfInterested(final Button join_btn, final String sid, final String fuser) { 

    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                final Interested interested = snapshot.getValue(Interested.class);
                assert interested != null;
                if (interested.getSid().equals(sid) && interested.getInterested().equals(fuser)) {
                    join_btn.setBackgroundColor(0xFFFF0000);
                }
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

private void checkIfAccepted(final Button join_btn, final String sid, final String fuser) {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                final Interested interested = snapshot.getValue(Interested.class);
                assert interested != null;
                if (interested.getSid().equals(sid) && interested.getAccepted().equals(fuser)) {
                    join_btn.setBackgroundColor(0xff00ff00);
                }
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

everything works good with firebase but buttons change color when they should not.

java
android
firebase
firebase-realtime-database
recycler-adapter
asked on Stack Overflow Oct 23, 2019 by Karol Ładuński • edited Oct 23, 2019 by Frank van Puffelen

2 Answers

1

First of all, you should not be using the onBindViewHolder()'s position parameter in your OnClickListener. This is unreliable and you should use holder.getAdapterPosition() instead.

Next, RecyclerViews re-use or "recycle" views which makes them efficient for having many views in a list. However, they have a problem if you want different items to have different background colors. The solution is to keep track of which ones you want to be "selected" or have a different background then update it accordingly. So:

if(item.isSelected())
    setBackgroundColor(<highlight_color>)
else
    setBackgroundColor(<default_color>)

You need to make sure ones that are not selected are still set to the default background because the recycler view might have accidentally used a highlighted one in its place.

answered on Stack Overflow Oct 23, 2019 by Jacob Kaddoura
1

RecyclerView used ViewHolder design pattens. That's why it's view is reused between different items. So, your call to holder.join.getBackground() don't give you the exact result for that item. To avoid you should store your color inside model [here: Suggestion] and make decision based on that.

Step - 1:

class Suggestion {

    ...

    private int buttonColor = 0xF0DC82;

    public int getButtonColor() {
        return buttonColor;
    }

    public void setButtonColor(int buttonColor) {
        this.buttonColor = buttonColor;
    }

    ...

}

Step - 2: update your checkIfAccepted and checkIfAccepted like below:

private void checkIfAccepted(Suggestion suggestion, final String fuser) {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                final Interested interested = snapshot.getValue(Interested.class);
                assert interested != null;
                if (interested.getSid().equals(suggestion.getSid()) && interested.getAccepted().equals(fuser)) {
                    suggestion.setButtonColor(0xff00ff00);
                }
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

private void checkIfAccepted(Suggestion suggestion, final String fuser) {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");
    reference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                final Interested interested = snapshot.getValue(Interested.class);
                assert interested != null;
                if (interested.getSid().equals(suggestion.getSid()) && interested.getAccepted().equals(fuser)) {
                    suggestion.setButtonColor(0xff00ff00);
                }
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });
}

Step - 3: Then change your onBindViewHolder implementation:

@Override
public void onBindViewHolder(@NonNull final SuggestionHomeAdapter.ViewHolder holder, final int position) {

    final Suggestion suggestion = mSuggestion.get(position);
    fuser = FirebaseAuth.getInstance().getCurrentUser();

    holder.title.setText(suggestion.getTitle());
    holder.description.setText(suggestion.getDescription());
    holder.join.setBackgroundColor(suggestion.getButtonColor());
    getUserInfo(holder.username, holder.profile_image, suggestion.getSpublisher());

    checkIfInterested(suggestion, fuser.getUid());
    checkIfAccepted(suggestion, fuser.getUid());

    holder.join.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if(suggestion.getButtonColor() != 0xFFFF0000 && suggestion.getButtonColor() != 0xff00ff00){
                joinEvent(fuser.getUid(), suggestion);
                notifyItemChanged(position);
            }
        }
    });
}

Step - 4: Lastly update joinEvent

private void joinEvent(String fuser, Suggestion suggestion) {
    DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Interested");

    String iId = reference.push().getKey();

    HashMap<String, Object> map = new HashMap<>();
    map.put("interested", fuser);
    map.put("sid", suggestion.getSid());
    map.put("iId", iId);
    map.put("publisher", suggestion.getSpublisher());
    map.put("accepted", "");
    reference.child(iId).setValue(map);

    suggestion.setButtonColor(0xFFFF0000);
}
answered on Stack Overflow Oct 23, 2019 by Md. Asaduzzaman

User contributions licensed under CC BY-SA 3.0