【问题标题】:Problem of automatic change of data value when scrolling in RecyclerViewRecyclerView滚动时数据值自动变化的问题
【发布时间】:2021-03-16 18:50:15
【问题描述】:

我附上了一张图片来帮助你理解我的问题。

我正在制作一个锻炼应用程序。

当按钮被按下时,例程被添加 在添加的例程中,可以再次使用按钮添加详细数据(set、lbs、reps)。

所以,我使用的是two type RecyclerView(页脚除外)。

我使用DiffUtil 来更新正在添加的项目。

我在进行测试时发现了一个奇怪的现象。 (见照片)

这不是错误,所以我无法弄清楚它有什么问题。

输入数据后,当我添加了一些明细项时,首先输入的数据的值出现在后面添加的项中。

如果我在添加许多项目后滚动,数据会随机移动。

但是,当您添加大量项目或滚动时,会出现这种现象。

怎么了?


RoutineAdapter.java

public class RoutineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    final static int TYPE_ROUTINE = 1;
    final static int TYPE_ROUTINE_DETAIL = 2;
    final static int TYPE_ROUTINE_FOOTER = 3;

    private Context context;
    private List<Object> mItems = new ArrayList<>();
    OnRoutineItemClickListener routinelistener;
    OnRoutineAddClickListener routineAddListener;

    public void updateRoutineList(List<Object> newRoutineList) {
        final RoutineDiffUtil diffCallback = new RoutineDiffUtil(this.mItems, newRoutineList);
        final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallback);

        this.mItems.clear();
        this.mItems.addAll(newRoutineList);
        diffResult.dispatchUpdatesTo(this);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        context = parent.getContext();
        View itemView;
        if(viewType == TYPE_ROUTINE){
            itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
            return new RoutineViewHolder(itemView);
        }
        else if(viewType == TYPE_ROUTINE_DETAIL){
            itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item, parent, false);
            return new RoutineDetailViewHolder(itemView);
        }
        else {
            itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, false);
            return new RoutineAddFooterViewHolder(itemView);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Object obj;
        switch (getItemViewType(position)) {
            case TYPE_ROUTINE:
                obj = mItems.get(position);
                setRoutineData((RoutineViewHolder) holder, (RoutineModel) obj);
                break;
            case TYPE_ROUTINE_DETAIL:
                obj = mItems.get(position);
                RoutineDetailModel item = (RoutineDetailModel) obj;
                ((RoutineDetailViewHolder) holder).setDetailItem(item);
                break;
            case TYPE_ROUTINE_FOOTER:
                break;
        }
    }
    
    @Override
    public int getItemCount() {
        if(mItems == null)
            return -1;
        return mItems.size() + 1; // for footer
    }

    @Override
    public int getItemViewType(int position) {
        if(position == mItems.size()) {
            return TYPE_ROUTINE_FOOTER;
        }
        else {
            Object obj = mItems.get(position);
            if(obj instanceof RoutineModel) {
                return TYPE_ROUTINE;
            }
            else {
                // obj instanceof RoutineDetailModel
                return TYPE_ROUTINE_DETAIL;
            }
        }
    }
    
    // add routine interface
    public interface OnRoutineAddClickListener {
        public void onAddRoutineClick();
    }

    public void setOnAddRoutineClickListener(OnRoutineAddClickListener listener) {
        this.routineAddListener = listener;
    }
    
    // details add / remove interface
    public interface OnRoutineItemClickListener {
        public void onAddBtnClicked(int curRoutinePos);
        public void onDeleteBtnClicked(int curRoutinePos);
        public void onWritingCommentBtnClicked(int curRoutinePos);
    }

    public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
        this.routinelistener = listener;
    }

    private class RoutineViewHolder extends RecyclerView.ViewHolder {
        public TextView routine;
        public Button addSet;
        public Button deleteSet;
        public Button comment;

        public RoutineViewHolder(@NonNull View itemView) {
            super(itemView);

            initViews();

            addSet.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
                        routinelistener.onAddBtnClicked(getAdapterPosition());
                }
            });

            deleteSet.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
                        routinelistener.onDeleteBtnClicked(getAdapterPosition());
                }
            });

            comment.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(routinelistener != null && getAdapterPosition() != RecyclerView.NO_POSITION)
                        routinelistener.onWritingCommentBtnClicked(getAdapterPosition());
                }
            });
        }
    }

    private class RoutineDetailViewHolder extends RecyclerView.ViewHolder {
        public TextView set;
        public TextView weight;

        public RoutineDetailViewHolder(@NonNull View itemView) {
            super(itemView);
            initViews();
        }

        private void initViews() {
            set = itemView.findViewById(R.id.set);
            weight = itemView.findViewById(R.id.weight);
        }

        private void setDetailItem(RoutineDetailModel item) {
            set.setText(item.getSet().toString() + "set");
        }
    }
}

RoutineDiffUtil.java

public class RoutineDiffUtil extends DiffUtil.Callback {
    private List<Object> oldRoutineList;
    private List<Object> newRoutineList;

    public RoutineDiffUtil(List<Object> oldRoutineList, List<Object> newRoutineList) {
        this.oldRoutineList = oldRoutineList;
        this.newRoutineList = newRoutineList;
    }

    @Override
    public int getOldListSize() {
        return oldRoutineList.size();
    }

    @Override
    public int getNewListSize() {
         return newRoutineList.size();
    }

    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        Object oldObj = oldRoutineList.get(oldItemPosition);
        Object newObj = newRoutineList.get(newItemPosition);
        if (oldObj instanceof RoutineModel && newObj instanceof RoutineModel) {
            return ((RoutineModel) oldObj).id == ((RoutineModel) newObj).id;
        }
        else if (oldObj instanceof RoutineDetailModel && newObj instanceof RoutineDetailModel) {
            return ((RoutineDetailModel) oldObj).id == ((RoutineDetailModel) newObj).id;
        }
        else if(oldObj instanceof RoutineModel && newObj instanceof RoutineDetailModel) {
            return false;
        }
        else {
            return false;
        }
    }

    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        return (oldRoutineList.get(oldItemPosition)).equals(newRoutineList.get(newItemPosition));
    }
}

RoutineDetailModel.java

public class RoutineDetailModel {
    public int id;
    private int set = 1;
    private int weight;

    public RoutineDetailModel() {
        Random random = new Random();
        this.id = random.nextInt();
    }

    public RoutineDetailModel(int set) {
        Random random = new Random();
        this.id = random.nextInt();
        this.set = set+1;
    }
    public Integer getSet() {
        return set;
    }

    public int getId() {
        return id;
    }
    @Override
    public int hashCode() {
        return Objects.hash(set, weight);
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        if(obj != null && obj instanceof RoutineDetailModel) {
            RoutineDetailModel model = (RoutineDetailModel) obj;
            if(this.id == model.getId()) {
                return true;
            }
        }
        return false;
    }
}

【问题讨论】:

    标签: android android-recyclerview android-diffutils


    【解决方案1】:

    首先,您应该使用带有 diff util 的 ListAdapter 类。您的适配器的问题在于,Recycler 视图一次又一次地回收视图。也就是说,当您为第一个项目输入文本时,该项目将用于其他视图。要在任何文本更改后解决它,您应该将此文本保留在模型类中,然后您应该将此文本设置为 onBind() 方法中的字段。综上所述,recycler view 对不同的 item 使用相同的 view,因此任何 item 相关的数据都应该保存在模型中,并且应该在 onBind() 中将模型设置为视图。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 2020-11-30
    • 1970-01-01
    • 2017-11-13
    • 2017-02-05
    • 2018-11-06
    • 2018-09-14
    相关资源
    最近更新 更多