为什么在 Recycler View 中滚动后值会消失?

问题描述

enter image description here

滚动前的数据

enter image description here

滚动后的数据

enter image description here


我的应用程序的问题如上图所示。

输入数据后,如果我在将项目添加为可滚动后滚动,数据就会消失。

作为进一步的解释,有时输入的数据会出现在已添加的其他项目中。

为了解释该应用程序,它是一个运动记录应用程序,它使用 multi-type recycler view

我使用了 listadapterDiffUtil。并且图片Detail item有关。

TextWatcher 用于保存输入的数据。

我一直在寻找解决这个问题的方法

搜索最多的两个解决方案是here

  1. 使用getItemViewType()getItemId() ->我使用此链接中所示的方法,但没有解决问题。

  2. 在支架内使用 setIsRecyclable(false) -> 这个方法奏效了。但我听说 setIsRecyclable(false)一个没有 recycle函数。 如果我使用它,这不是一个很好的方法,因为使用 RecyclerView 没有优势吗?

RoutineAdapter.java

public class Routinelistadapter extends listadapter<Object,RecyclerView.ViewHolder> {
    Context context;
    Routinelistadapter.OnRoutineItemClickListener routinelistener;
    Routinelistadapter.OnRoutineAddClickListener routineAddListener;

    final static int TYPE_ROUTINE = 1;
    final static int TYPE_ROUTINE_DETAIL = 2;
    final static int TYPE_ROUTINE_FOOTER = 3;

    public Routinelistadapter(@NonNull DiffUtil.ItemCallback<Object> diffCallback) {
        super(diffCallback);
    }

    // add routine interface
    public interface OnRoutineAddClickListener {
        public void onAddRoutineClick();
    }

    public void setonAddRoutineClickListener(Routinelistadapter.OnRoutineAddClickListener listener) {
        this.routineAddListener = listener;
    }

    // add/remove detail interface
    public interface OnRoutineItemClickListener {
        public void onAddBtnClicked(int curRoutinePos);
        public void onDeleteBtnClicked(int curRoutinePos);
        public void onWritingCommentBtnClicked(int curRoutinePos);
    }

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

    @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 Routinelistadapter.RoutineViewHolder(itemView);
        }
        else if(viewType == TYPE_ROUTINE_DETAIL){
            itemView = LayoutInflater.from(context).inflate(R.layout.routine_detail_item,false);
            return new Routinelistadapter.RoutineDetailViewHolder(itemView);
        }
        else {
            itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item,false);
            return new Routinelistadapter.RoutineAddFooterViewHolder(itemView);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position) {
        Object curItem;
        switch (getItemViewType(position)) {
            case TYPE_ROUTINE:
                curItem = getItem(position);
                setRoutineData((Routinelistadapter.RoutineViewHolder) holder,(RoutineModel) curItem);
                break;
            case TYPE_ROUTINE_DETAIL:
                curItem = getItem(position);
                RoutineDetailModel item = (RoutineDetailModel) curItem;
                ((Routinelistadapter.RoutineDetailViewHolder) holder).bind(item);
                ((RoutineDetailViewHolder) holder).weight.addTextChangedListener(new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence s,int start,int count,int after) {

                    }

                    @Override
                    public void onTextChanged(CharSequence s,int before,int count) {

                    }

                    @Override
                    public void afterTextChanged(Editable s) {
                        item.setWeight(((RoutineDetailViewHolder) holder).weight.getText().toString());
                    }
                });
                break;
            case TYPE_ROUTINE_FOOTER:
                break;
        }
    }

    private void setRoutineData(Routinelistadapter.RoutineViewHolder holder,RoutineModel routineItem){
        holder.routine.setText(routineItem.getRoutine());
    }

    public Object getRoutineItem(int position) {
        if(getCurrentList() == null || position < 0 || position >= getCurrentList().size())
            return null;
        return getItem(position);
    }

    @Override
    public int getItemCount() {
        return getCurrentList().size() + 1;
    }

    @Override
    public int getItemViewType(int position) {
        if(position == getCurrentList().size()) {
            return TYPE_ROUTINE_FOOTER;
        }
        else {
            Object obj = getItem(position);
            if(obj instanceof RoutineModel) {
                return TYPE_ROUTINE;
            }
            else {
                // obj instanceof RoutineDetailModel
                return TYPE_ROUTINE_DETAIL;
            }
        }
    }

    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);
            routine = itemView.findViewById(R.id.routine);
            addSet = itemView.findViewById(R.id.add_set);
            deleteSet = itemView.findViewById(R.id.delete_set);
            comment = itemView.findViewById(R.id.write_comment);

            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 {
        private TextView set;
        private EditText weight;

        public RoutineDetailViewHolder(@NonNull View itemView) {
            super(itemView);
            set = itemView.findViewById(R.id.set);
            weight = itemView.findViewById(R.id.weight);
        }

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

    private class RoutineAddFooterViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public RoutineAddFooterViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.add_text);
            ConstraintLayout regionForClick = itemView.findViewById(R.id.clickable_layout);
            regionForClick.setonClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (routineAddListener != null) {
                        routineAddListener.onAddRoutineClick();
                    }
                }
            });
        }
    }
}

更新

适配器

public class Routinelistadapter extends listadapter<Object,RecyclerView.ViewHolder> {

//  detail add / remove iterface
    public interface OnRoutineItemClickListener {
        public void onAddBtnClicked(int curRoutinePos);
        public void onDeleteBtnClicked(int curRoutinePos);
        public void onWritingCommentBtnClicked(int curRoutinePos);
        public void onWritingWeight(int curRoutinePos,View view); // write weight
    }

    public void setonRoutineClickListener(Routinelistadapter.OnRoutineItemClickListener listener) {
        if(this.routinelistener != null)
            this.routinelistener = null;
        this.routinelistener = listener;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,(RoutineModel) curItem);
                break;
            case TYPE_ROUTINE_DETAIL:
                ((Routinelistadapter.RoutineDetailViewHolder) holder).bind();
                break;
            case TYPE_ROUTINE_FOOTER:
                break;
        }
    }

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

        public RoutineDetailViewHolder(@NonNull View itemView) {
            super(itemView);
            set = itemView.findViewById(R.id.set);
            weight = itemView.findViewById(R.id.weight);

            weight.addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence s,int after) {

                }

                @Override
                public void onTextChanged(CharSequence s,int count) {
                    
                }

                @Override
                public void afterTextChanged(Editable s) {
                  routinelistener.onWritingWeight(getAdapterPosition(),itemView);
                }
            });
        }

        private void bind() {
            RoutineDetailModel item = (RoutineDetailModel) getItem(getAdapterPosition());
            set.setText(item.getSet().toString() + "set");
            weight.setText(item.getWeight()); // Setting the saved value
        }
    }

活动

public class WriteRoutineActivity extends AppCompatActivity implements WritingCommentDialogFragment.OnDialogClosedListener {
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_write_routine);

        initViews();
        setPageTitle(getIntent());
        setRoutineRecyclerview();

        diffUtil2 = new RoutineDiffUtil2();
        listadapter = new Routinelistadapter(diffUtil2);
        items = new ArrayList<>();
        routine_rv.setAdapter(listadapter);

        listadapter.setonRoutineClickListener(new Routinelistadapter.OnRoutineItemClickListener() {
            @Override
            public void onWritingWeight(int curRoutinePos,View v) {
                RoutineDetailModel item = (RoutineDetailModel) listadapter.getRoutineItem(curRoutinePos);
                EditText weight = v.findViewById(R.id.weight);
                item.setWeight(weight.getText().toString()); // This is saved to set the value again when recycled.
        });
    }
}

如果您需要任何其他额外代码,请告诉我

解决方法

问题在于您在 TextWatcher 中添加的 onBindViewHolder

目前您已经设置好每次 RecyclerView 绑定视图(每个实际视图可能发生多次),您添加一个新的 TextWatcher 然后还设置文本到项目的权重,然后触发您之前添加的观察者,将项目的权重设置为其他内容,在本例中为空字符串。

您应该做的是在添加另一个监听器之前移除所有监听器,或者在 onCreateViewHolder 中添加监听器并使用支架的适配器位置来获取您的项目。


这是一些伪代码来阐明我的建议:

onCreateViewHolder

中添加监听器
RoutineDetailViewHolder {
    private EditText weight;

    RoutineDetailViewHolder {

        weight.addTextChangedListener {
            
            items[adapterPosition].setWeight(...)
        }
    }
}

在再次绑定之前移除监听器:

RoutineDetailViewHolder {
    private EditText weight;
    private TextWatcher weightWatcher;

    void bind() {
    
        weight.removeTextChangedListener(weightWatcher);
        
        weightWatcher = new TextWatcher();
        weight.addOnTextChangedListener(weightWatcher);
    }
}