RecyclerView 中的 onClick 按钮适用于多个视图而不是自身

onClick button inside RecyclerView works for multiple view instead of itself

我正在编写一个翻译应用程序 words.There 大约 40 个单词合而为一 RecyclerView。每个单词都有一个按钮显示该单词的含义。 我为 onBindViewHolder 内的那个按钮写了 onClickListener,如下所示:

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        final int pos = position;
        holder.tv_wordText.setText(words.get(pos).getWord());
        holder.tv_meaningText.setText(words.get(pos).getMeaning());

        holder.btn_showAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                holder.tv_meaningText.setVisibility(View.VISIBLE);
                holder.btn_showAnswer.setVisibility(View.INVISIBLE);
            }
        });
    }

但是当我点击那个按钮时,它不仅会显示自己的意思,还会每隔 5 个单词显示下一个。例如,如果我点击第一个单词,它会显示 1,6,11,16,21 等等。第二个按钮也显示 2,7,12,17,22。应用程序的所有其他部分都可以正常工作,但那个按钮不能正常工作,我不知道为什么?我也在 ViewHolder class 里面做了,但是效果一样。有没有正确的方法?

3 天后,我发现了一个适合我的棘手代码。 我意识到每次滚动 RecyclerView 时,onBindViewHolder 运行 中的每个可见 RecyclerView 中的 ViewHolder 代码。所以我决定在 onBindViewHolder 中放入隐藏该元素的代码:

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        final int pos = position;
        holder.tv_wordText.setText(words.get(pos).getWord());
        holder.tv_meaningText.setText(words.get(pos).getMeaning());
        holder.tv_meaningText_cv.setVisibility(View.INVISIBLE);
        
        holder.btn_showAnswer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                holder.tv_meaningText_cv.setVisibility(View.VISIBLE);
            }
        });
    }

但它也使所有其他元素不可见。
如果您有更好的解决方案,请在这里提出。

这与 RecyclerView 回收 行为有关。当您将视图滚动到屏幕的一个边缘之外时,系统最终将 re-use 该视图用于您的单词列表中的另一个位置。由于您只是有条件地设置某些视图的可见性,因此最后设置的任何内容都将保留在 re-used 视图中。因此,如果您单击以显示一个词的含义,下一次该视图是 re-used,您将能够看到新词的含义。

对于 RecyclerView,最好始终让您的数据模型(您的 words 列表)能够完全代表您的状态。我不知道 words 中的每个元素到底是什么样子,但我可以看到每个元素都有一个 getWord() 和一个 getMeaning() 函数。我认为您也应该为此添加一个 isMeaningVisible(): Boolean 函数。然后你可以有这样的东西:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    Word word = words.get(position);
    holder.tv_wordText.setText(word.getWord());
    holder.tv_meaningText.setText(word.getMeaning());
    holder.tv_meaningText_cv.setVisibility(word.isMeaningVisible() ? View.VISIBLE : View.INVISIBLE);
}

另一个问题是如何处理按钮上的点击。我注意到在您的代码中有 final int pos;这不是一个好主意。 RecyclerView 允许您插入或删除单个元素,因此传递给 onBindViewHolder() 的位置可以随时间变化。因此,最好在 onCreateViewHolder() 中设置点击侦听器。

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    ViewHolder holder = new ViewHolder(LayoutInflater.from(context).inflate(R.layout.word_list_adapter, parent, false));

    holder.btn_showAnswer.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int position = holder.getAdapterPosition();
            if (position != RecyclerView.NO_POSITION) {
                Word word = words.get(position);
                word.setMeaningVisible(!word.isMeaningVisible());
                notifyItemChanged(position);
            }
        }
    });

    return holder;
}