Recyclerview + Listadapter中的项目不会在更新时重绘

时间:2019-06-13 22:53:29

标签: java android android-recyclerview android-listadapter

我正在设置一个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

}

1

单击“收藏夹”按钮(心形)之前的列表。相关的是心脏和眼睛按钮。

2

加入“Akademiestraße”项目之后的列表。它已经改变了位置,但是心脏图标没有改变并且OnClickListeners仍然相同。

3

滚动并返回列表顶部后的列表。现在,心已充满,并且OnClickListeners已更改。

2 个答案:

答案 0 :(得分:0)

在我看来,您的数据正在更新,但是RecyclerView仅更新订单,而不更新项目的视图。更新视图中的项目后,尝试调用适配器的notifyDataSetChanged()

答案 1 :(得分:0)

请记住,您的视图正在被回收,这意味着如果您有一个检查位置为 0 的复选框。滚动将使该复选框在某些项目中重复使用,因此您可能会看到其他项目也被选中你永远不会检查它。始终保存视图的状态,因为它将被回收。您可以使用您的 POJO/模型来保存带有布尔字段的状态。

此外,在使用 DiffUtil 时,请确保它是一个不同的列表实例,不要重用旧的实例,因为它可能不会更新您的数据。

您可能还想将此 adapter.submitList(new LinkedList<>(mensas)); 更改为仅此 adapter.submitList(mensas);