Reputation: 11071
I become crazy with databinding in RecyclerView. The problem is that it binds data to multiple rows and I can't figure out why.
I simplified my code as much as possible. When we start the application there is a 200 rows with a label (empty value by default) and a button. When I click a button my label changes it's value.
So here is a opened activity. As you can see no label is displayed.
Let's click a button at the first row
As expected label appearned at correct position. Let's scroll now.
We can see that label appeared also at 11th position, 21st, etc. Any ideas why?
My Adapter:
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
ListLearnItemBinding itemBinding = ListLearnItemBinding.inflate(layoutInflater, parent, false);
return new LearnItemViewHolder(itemBinding);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
final LearnItemViewHolder viewHolder = (LearnItemViewHolder) holder;
// init binding
if(viewHolder.getBinding().getItem2() == null){
TestBinding t = new TestBinding();
t.setRating("");
viewHolder.getBinding().setItem2(t);
}
viewHolder.getBinding().test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
TestBinding item2 = new TestBinding();
item2.setRating("Label");
viewHolder.bind(item2);
}
});
}
@Override
public int getItemCount() {
return 200;
}
ViewHolder:
public class LearnItemViewHolder extends RecyclerView.ViewHolder {
public ListLearnItemBinding getBinding() {
return binding;
}
private final ListLearnItemBinding binding;
public LearnItemViewHolder(ListLearnItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(TestBinding testItem) {
binding.setItem2(testItem);
binding.executePendingBindings();
}
}
Binding model
public class TestBinding {
public String getRating() {
return rating;
}
public void setRating(String rating) {
this.rating = rating;
}
private String rating;
}
OnCreate of my view
binding.recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
binding.recyclerView.setItemAnimator(new DefaultItemAnimator());
LearnAdapter mAdapter = new LearnAdapter(this, data);
binding.recyclerView.setAdapter(mAdapter);
Layout
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(item2.rating)}" />
<Button
android:id="@+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="20dp"
/>
Upvotes: 0
Views: 320
Reputation: 4737
This happens because when you're scrolling old views are being resused for optimization. You need something like this :
private List<String> mList;
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
final LearnItemViewHolder viewHolder = (LearnItemViewHolder) holder;
TestBinding t = new TestBinding();
t.setRating(mList.get( position);
viewHolder.bind(t);
viewHolder.getBinding().test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
TestBinding item2 = new TestBinding();
String label ="Label";
item2.setRating(label);
mList.set(position, label);
viewHolder.bind(item2);
}
});
}
@Override
public int getItemCount() {
return mList.size();
}
Upvotes: 1