为什么在 Recycler View 中滚动后值会消失? [英] Why do values ​disappear after scrolling in Recycler View?

查看:50
本文介绍了为什么在 Recycler View 中滚动后值会消失?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

滚动前的数据

滚动后的数据

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

The problem with my app is shown in the pictures above.

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

After entering the data, if i scroll after adding the item as scrollable, the data disappears.

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

As a further explanation, sometimes the entered data appears in other items that have been added.

解释一下这个应用程序,它是一个运动记录应用程序,它使用了多类型回收器视图.

To explain the app, it is an exercise recording app, and it uses a multi-type recycler view.

我使用了 ListAdapterDiffUtil.而图片与Detail item相关.

I used ListAdapter and also DiffUtil. And the picture is related to the Detail item.

TextWatcher 用于保存输入的数据.

TextWatcher was used to save the entered data.

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

I've been searching a lot to solve this.

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

The two most searched solutions are here

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

  1. using getItemViewType(), getItemId() ->I used this method as shown in this link, but it didn't solve the problem.

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

Using setIsRecyclable(false) inside the holder -> This method worked. But I heard that setIsRecyclable(false) is a function that does not recycle. If i use this, isn't it a good way to do it because there is no advantage to using 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, parent, false);
            return new RoutineListAdapter.RoutineDetailViewHolder(itemView);
        }
        else {
            itemView = LayoutInflater.from(context).inflate(R.layout.add_routine_item, parent, 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 start, 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, int position) {
        Object curItem;
        switch (getItemViewType(position)) {
            case TYPE_ROUTINE:
                curItem = getItem(position);
                setRoutineData((RoutineListAdapter.RoutineViewHolder) 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 start, int count, int after) {

                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before, 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.
        });
    }
}

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

推荐答案

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

The issue lies with your TextWatcher that you're adding in onBindViewHolder.

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

At the moment you have it setup so that every time the RecyclerView binds a view (which can happen multiple times per actual view), you're adding a new TextWatcher and then also setting the text to the item's weight, which is then triggering the previous watchers you added, setting the item's weight to something else, in this case an empty string.

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

What you should be doing is either removing all listeners before you add another one, or add the listener in onCreateViewHolder and use the holder's adapter position to get your item.

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

Here is some pseudocode to clarify my suggestions:

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);
    }
}

这篇关于为什么在 Recycler View 中滚动后值会消失?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆