且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

从recyclerview项目中将选定的RadioButton文本保存在模型类中

更新时间:2023-12-01 22:36:46

问题是,尽管表面上代码看起来正确,但实际上发生的是当您调用 holder.radioGroup.check(),它会触发 onCheckedChanged()事件处理程序,就像用户启动它一样.

The problem is that although the code looks correct on the surface, what's actually happening is that when you call holder.radioGroup.check(), it triggers your onCheckedChanged() event handler just the same as if the user had initiated it.

由于视图被回收,因此位置0的视图将重新用于列表中的位置8.因此,对 onBindViewHolder()中的 check()的调用将调用 onCheckedChanged(),其中从位置0开始选中的单选按钮仍处于选中状态(即 checkedId radioGroup.getCheckedRadioButtonId()将返回在位置0使用视图时选中的单选按钮的ID.

Since the views are recycled, the view at position 0 is being reused for position 8 in the list. So the call to check() in onBindViewHolder() will call onCheckedChanged(), with the checked radio button from position 0 still checked (i.e. checkedId and radioGroup. getCheckedRadioButtonId() will return the ID of the radiobutton checked when the view was used at position 0).

真正的症结所在是

models.get(clickedPos).setChecked(radioButtonID);

考虑答案的第一段,您将意识到这将(错误地)使用在位置0处使用此视图时检查过的 radioButtonID 更新位置8处的模型项.

Consider the first paragraphs of the answer, and you'll realize that this will (incorrectly) update the model item at position 8 with the radioButtonID that was checked when this view was used at position 0.

解决此问题的一种方法是区分用户发起的更改和绑定发起的更改.例如,您可以通过在 ViewHolder 中添加一个字段以指示视图当前是否绑定来完成此操作.

One way to solve this is to distinguish between a user-initiated change and a binding-initiated change. You can for example do this by adding a field to the ViewHolder to indicate if the view is currently binding.

class ViewHolder extends RecyclerView.ViewHolder{
    TextView selectedAnswer;
    RadioGroup radioGroup;

    boolean isBinding;

    ViewHolder(View itemView) {
        super(itemView);

        radioGroup = itemView.findViewById(R.id.radioGroup);

        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                int position = getAdapterPosition();

                RadioButton radioButton = (RadioButton) group.findViewById(checkedId);

                /* Only update the model when onCheckedChange() was initiated by the user clicking
                   a radio button, not when the adapter is binding the view. In that scenario, we
                   are only interested in passing information FROM the model TO the view. */
                if( !isBinding ) {
                    models.get(position).setChecked(checkedId);
                    models.get(position).setSelectedAns(radioButton != null ? radioButton.getText().toString() : "");
                }

                selectedAnswer.setText(  models.get(position).getSelectedAns() );
            }
        });

        ...
    }
}

 

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
    holder.isBinding = true;

    ...

    /* When calling check() here, we invoke onCheckedChanged(), which will
       update the textview that displays the selected answer - so no need to call
           holder.selectedAnswer.setText(  models.get(position).getSelectedAns() )
       from here */
    holder.radioGroup.check(models.get(position).getChecked());

    holder.isBinding = false;
}