我正在设置一个RecyclerView,它使用ListAdapter来计算更改时的动画。 RecyclerView通过ViewModel接收数据,该ViewModel通过Firebase获取列表。显示的项目为Mensa
类型。 Mensa物品可以更改其可见性,占用率或显示的距离。
我想实现两个按钮,它们收藏/隐藏项目,因此更改了它们在列表中的位置。每个项目中的两个按钮允许用户收藏或隐藏一个项目。根据排序策略,这会将项目移到列表的顶部/底部,该方法将“收藏夹”放在第一位,默认设置在第二位,隐藏在最后。
但是,当我单击按钮时,列表将重新排列,但是单击的项目不会重新绑定。按钮保留其旧状态(和OnClickListeners),只有滚动列表才会调用onBind方法。 DiffUtil.Callback是我的问题吗?我真的不知道我的代码有什么问题。
我已经在适配器的SubmitList方法中提供了一个新列表(本例中另一个stackoverflow问题的建议启用了动画),但是单击的项目仍然不会重绘。
在MensaListActivity.java中
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mensa_list);
viewModel = ViewModelProviders.of(this).get(MensaListModel.class);
final RecyclerView recyclerView =findViewById(R.id.mensa_list_recyclerview);
final MensaListAdapter adapter = new MensaListAdapter(this, new MensaListAdapter.ItemButtonsListener() {
@Override
public void visibilityButtonClicked(Mensa mensa, VisibilityPreference newVisibility) {
viewModel.visibilityChanged(mensa, newVisibility);
}
});
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(false);
recyclerView.setLayoutManager(new GridLayoutManager(this, 1, RecyclerView.VERTICAL, false));
viewModel.getMensaData().observe(this, new Observer<LinkedList<Mensa>>() {
@Override
public void onChanged(LinkedList<Mensa> mensas) {
adapter.submitList(new LinkedList<>(mensas));
}
});
在MensaListModel.java中
public LiveData<LinkedList<Mensa>> getMensaData() {
return mensaData;
}
// ...
public void visibilityChanged(Mensa changedItem, VisibilityPreference newVisibility) {
LinkedList<Mensa> newData = getMensaData().getValue();
int index = newData.indexOf(changedItem);
newData.remove(index);
newData.add(changedItem);
sortMensaData(newData);
// sortMensaData calls postValue method
MensaListAdapter.java
public class MensaListAdapter extends ListAdapter<Mensa, MensaListAdapter.MensaViewHolder> {
private final ItemButtonsListener listener;
private final Context context;
class MensaViewHolder extends RecyclerView.ViewHolder {
TextView nameLabel;
TextView addressLabel;
TextView restaurantTypeLabel;
TextView occupancyLabel;
TextView distanceLabel;
ImageButton favoriteButton;
ImageButton hideButton;
public MensaViewHolder(@NonNull View itemView) {
super(itemView);
// a bunch of assignments
}
public void bindData(final Mensa newMensa) {
nameLabel.setText(newMensa.getName());
addressLabel.setText(newMensa.getAddress());
restaurantTypeLabel.setText(newMensa.getType().toString());
String occText = "Occupancy: " + newMensa.getOccupancy().toInt();
occupancyLabel.setText(occText);
if (newMensa.getDistance() != -1) {
distanceLabel.setVisibility(View.VISIBLE);
distanceLabel.setText(Double.toString(newMensa.getDistance()));
} else {
distanceLabel.setVisibility(View.INVISIBLE);
}
switch(newMensa.getVisibility()){
case FAVORITE:
favoriteButton.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.favorite_active, null));
favoriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.visibilityButtonClicked(newMensa, VisibilityPreference.DEFAULT);
}
}); break;
case DEFAULT:
favoriteButton.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.favorite_inactive, null));
favoriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.visibilityButtonClicked(newMensa, VisibilityPreference.FAVORITE);
}
}); break;
case HIDDEN:
favoriteButton.setImageDrawable(ResourcesCompat.getDrawable(context.getResources(), R.drawable.favorite_inactive, null));
favoriteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.visibilityButtonClicked(newMensa, VisibilityPreference.FAVORITE);
}
}); break;
// removed hidebutton assignments, as they're identical to the favoritebutton assignment
}
}
}
public MensaListAdapter(Context context, ItemButtonsListener listener) {
super(DIFF_CALLBACK);
this.context = context;
this.listener = listener;
}
private static final DiffUtil.ItemCallback<Mensa> DIFF_CALLBACK =
new DiffUtil.ItemCallback<Mensa>() {
@Override
public boolean areItemsTheSame(@NonNull Mensa oldItem, @NonNull Mensa newItem) {
return oldItem.equals(newItem);
}
@Override
public boolean areContentsTheSame(@NonNull Mensa oldItem, @NonNull Mensa newItem) {
return oldItem.getDistance() == newItem.getDistance()
&& oldItem.getOccupancy().equals(newItem.getOccupancy())
&& oldItem.getVisibility().equals(newItem.getVisibility());
}
};
@Override
public int getItemViewType(int position) {
return R.layout.mensa_list_item;
}
@NonNull
@Override
public MensaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new MensaViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MensaViewHolder holder, int position) {
holder.bindData(getItem(position));
}
public interface ItemButtonsListener{
void visibilityButtonClicked(Mensa mensa, VisibilityPreference newVisibility);
}
}
Mensa.java
public class Mensa {
private String uID;
private String name;
private String address;
private Occupancy occupancy;
private RestaurantType type;
private VisibilityPreference visibility;
private double latitude;
private double longitude;
// distance is calculated lazily as soon as location coordinates are available, -1 means not calculated.
private double distance = -1;
public Mensa() {
}
// generated by android studio
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Mensa mensa = (Mensa) o;
return uID.equals(mensa.uID);
}
@Override
public int hashCode() {
return Objects.hash(uID);
}
// a bunch of getters and setters
}
单击“收藏夹”按钮(心形)之前的列表。相关的是心脏和眼睛按钮。
加入“Akademiestraße”项目之后的列表。它已经改变了位置,但是心脏图标没有改变并且OnClickListeners仍然相同。
滚动并返回列表顶部后的列表。现在,心已充满,并且OnClickListeners已更改。
答案 0 :(得分:0)
在我看来,您的数据正在更新,但是RecyclerView仅更新订单,而不更新项目的视图。更新视图中的项目后,尝试调用适配器的notifyDataSetChanged()
。
答案 1 :(得分:0)
请记住,您的视图正在被回收,这意味着如果您有一个检查位置为 0 的复选框。滚动将使该复选框在某些项目中重复使用,因此您可能会看到其他项目也被选中你永远不会检查它。始终保存视图的状态,因为它将被回收。您可以使用您的 POJO/模型来保存带有布尔字段的状态。
此外,在使用 DiffUtil 时,请确保它是一个不同的列表实例,不要重用旧的实例,因为它可能不会更新您的数据。
您可能还想将此 adapter.submitList(new LinkedList<>(mensas));
更改为仅此 adapter.submitList(mensas);