1

I have a RecyclerView and respective adapter in which I created OnItemClickListener interface.

I try to pass the (onclicked) view itself to the implementation in the Activity and it gets multiple items. In fact, the change I want to make on an view item affects more than one item in the recycler view list. I want to change the background of an item in the list, but another item's background changes, too.

Here is the adapter:

public class TranslationAdapter extends RecyclerView.Adapter<TranslationAdapter.TranslationHolder> {
private List<String> translations = new ArrayList<>();

private OnItemClickListener listener;

@NonNull
@Override
public TranslationHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.from(viewGroup.getContext())
            .inflate(R.layout.translation_item, viewGroup, false);

    return new TranslationHolder(itemView);
}

@Override
public void onBindViewHolder(@NonNull TranslationHolder translationHolder, int i) {
...
}

public void setTranslations(List<String> _translations){
    this.translations = _translations;
    notifyDataSetChanged();
}

class TranslationHolder extends RecyclerView.ViewHolder{
    private TextView tvTranslation;

    public TranslationHolder(@NonNull final View itemView) {
        super(itemView);
        tvTranslation = itemView.findViewById(R.id.text_view_translation);

        itemView.setOnClickListener(v -> {
            int position = getAdapterPosition();
            if (listener != null && position != RecyclerView.NO_POSITION){
                listener.onItemClick(v, translations.get(position));
            }
        });
    }
}

public interface OnItemClickListener{
    void onItemClick(View view, String string);
}

public void setOnItemClickListener(OnItemClickListener listener){
    this.listener = listener;
}
}

and here is the implementation in the Activity:

public class TranslationActivity extends AppCompatActivity {
     RecyclerView recyclerView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_translation);

    recyclerView = findViewById(R.id.recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setHasFixedSize(true);

    recyclerView.setAdapter(adapter);        
}
 @Override
protected void onStart() {
    super.onStart();
     adapter.setOnItemClickListener((v, translation) -> {
        if (arrResultOfTranslation.contains(translation)){
            arrResultOfTranslation.remove(translation);
            v.setBackgroundColor(Color.BLUE);
        }
        else {
            v.setBackgroundColor(Color.YELLOW);
            arrResultOfTranslation.add(translation);
        }
    });
    }
}

As result, although I get only one string item (translation), multiple items' background color change. When I debug I see only one View instance (variable v).

Jake Lee
  • 7,549
  • 8
  • 45
  • 86
U.Savas
  • 129
  • 11
  • 1
    you cannot just use `v.setBackgroundColor()` in your listener - it has to be done inside `onBindViewHolder()` method, this is because your `v` view is reused by `RecyclerView` – pskink Jan 14 '19 at 10:27
  • May help you: https://stackoverflow.com/questions/36369913/how-to-implement-multi-select-in-recyclerview – Liu Silong Jan 14 '19 at 10:54
  • @pskink unfortunately putting the code in 'onBindViewHolder()' did not help. Still getting the same results, which is really curious, clicking on an item and setting the background color of another irrelevant item. – U.Savas Jan 14 '19 at 11:28
  • as said by Ahmed Karam below, instead of `private List translations` make `private List translations` where `TranslationItem` class keeps `String` and `boolean` fields and inside `onBindViewHolder` check that boolean field and call `v.setBackgroundColor()` either with `Color.BLUE` or `Color.YELLOW` – pskink Jan 14 '19 at 11:32

2 Answers2

1

Try this

RecyclerViewAct.java

public class RecyclerViewAct extends Activity {
    private static final String TAG = "RecyclerViewAct";
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycler_main);
        recyclerView = findViewById(R.id.recycler_view);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        MyAdapter adapter = new MyAdapter();

        List<String> data = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            data.add("item" + i);
        }

        adapter.setData(data);
        recyclerView.setAdapter(adapter);

        adapter.setOnClickListener(new MyAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(List<String> results) {
                Log.e(TAG, "onItemClick: " + results.size());
            }
        });

    }
}

MyAdapter.java

    class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyHolder> {

    private List<String> data = new ArrayList<>();
    private List<String> results = new ArrayList<>();

    public void setData(List<String> data) {
        this.data = data;
        notifyDataSetChanged();
    }

    @NonNull
    @Override
    public MyHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);
        return new MyHolder(itemView);
    }

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

        holder.textView.setText(data.get(position));


        if (results.contains(data.get(position))) {
            holder.textView.setBackgroundColor(Color.BLUE);
        } else {
            holder.textView.setBackgroundColor(Color.WHITE);
        }

        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String str = data.get(position);
                if (results.contains(data.get(position))) {
                    results.remove(str);
                    holder.textView.setBackgroundColor(Color.WHITE);
                } else {
                    holder.textView.setBackgroundColor(Color.BLUE);
                    results.add(str);
                }

                // selected list
                if (onClickListener != null) {
                    onClickListener.onItemClick(results);
                }

            }
        });
    }

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

    OnItemClickListener onClickListener;

    public interface OnItemClickListener {
        void onItemClick(List<String> results);

    }

    public void setOnClickListener(OnItemClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }

    class MyHolder extends RecyclerView.ViewHolder {
        TextView textView;

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

            textView = itemView.findViewById(R.id.tv_item);

        }

    }
}
Liu Silong
  • 4,902
  • 3
  • 19
  • 28
  • this answer helped me solve the problem, with a few further touches for customization. simple yet powerful! I wonder though how other people handle with this "bug". there must be some best practice for this, but could not find any. – U.Savas Jan 14 '19 at 15:44
0

The problem here is that the recycler reuse it's view,

My suggestion is to make a Pojo with your string and flag may be boolean and when you click this item set the boolean flag as true

in your onBindViewholder check if the flag is true set your custom properties you need

Ahmed Karam
  • 386
  • 1
  • 7