Android RecyclerView 中多Item的EditText,RadioButton,CheckBox焦点混乱,数据错乱的解决方法

最近在做项目时,UI设计师给了一个设计图,添加多个人的信息时,item里面包含了EditText在填写姓名时出现数据混乱,如下图所示:

 

 

RecyclerView中EditText的监听处理

这个界面的主要逻辑是监听每个EditText的输入,然后保存到个人对象里,最后保存到服务器上。RecyclerView中Item里EditText的监听可以按如下代码实现:
首先定义个回调接口:

 public interface OnTextChangeListener{
        void onClickItem(int position,EditTextBean bean);
    }

 在自定义Adapter中添加一个如上自定义的接口成员变量mTextListener,然后在onBindViewHolder方法里面添加Item的EditText输入监听,即EditText.addTextChangedListener(),然后在监听事件回调里的afterTextChanged 方法中 调用 mTextListener.onClickItem来达到接口回调的目的。

自定义EditListAdapter类:

/**
 * Created by ysp
 * on 2020/12/22
 */
public class EditListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context mContext;
    private LayoutInflater mInflater;
    private ArrayList<EditTextBean> mList;
    private OnTextChangeListener   mTextListener;

    public EditListAdapter(Context mContext, ArrayList<EditTextBean> mList) {
        this.mContext = mContext;
        mInflater=LayoutInflater.from(mContext);
        this.mList = mList;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view=mInflater.inflate(R.layout.item_edittext_refresh,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
          ViewHolder viewHolder= (ViewHolder) holder;
          if (mList.size()<=0){
              return;
          }
        final EditTextBean editTextBean = mList.get(position);
          viewHolder.tv_title.setText(editTextBean.getTitle());
          viewHolder.et_content.setText(editTextBean.getContent());
          viewHolder.et_content.setHint("请填写");
        //添加EditText的监听事件
        viewHolder.et_content.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                editTextBean.setContent(editable.toString());
                mTextListener.onClickItem(position,editTextBean);

            }
        });

    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{
        private TextView tv_title;
        private EditText et_content;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            tv_title=itemView.findViewById(R.id.tv_title);
            et_content=itemView.findViewById(R.id.et_content);
        }
    }

    public interface OnTextChangeListener{
        void onClickItem(int position,EditTextBean bean);
    }

    public void setOnTextChangeListener(OnTextChangeListener onTextChangeListener){
        mTextListener=onTextChangeListener;
    }
}

Activity中的代码实现,在mAdapter中添加匿名内部类来监听EditText中的接口回调数据:

public class EditTextRadioGroupActivity extends AppCompatActivity implements View.OnClickListener {
    private RecyclerView recyclerView;
    private EditListAdapter adapter;
    private TextView tv_add;
    private ArrayList<EditTextBean>list=new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_text_radio_group);
        recyclerView=findViewById(R.id.recyclerView);
        tv_add=findViewById(R.id.tv_add);
        tv_add.setOnClickListener(this);
        adapter=new EditListAdapter(this,list);
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);
        adapter.setOnTextChangeListener(new EditListAdapter.OnTextChangeListener() {
            @Override
            public void onClickItem(int position, EditTextBean bean) {
                list.set(position,bean);
            }
        });

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.tv_add:
                EditTextBean bean=new EditTextBean("姓名","");
                list.add(bean);
                adapter.notifyDataSetChanged();
                break;
        }
    }
}

OK 上面的监听事件处理完了 我们来试试效果怎么样。首先点击按钮添加一条数据,填写一下第一个人的名字:小米;然后再点击按钮添加一条数据,填写一下第二个人的名字:小兰;然后再点击按钮添加一条数据,你会发现第一个人的名字也变成了小兰;就出现上图中的监听事件数据错乱的重大的Bug。

Recycler中 多Item中EditText焦点混乱网上的解决方法

现在网上的解决方法很多是使用EditText的TAG来解决问题,EditText中有setTag和getTag可以储存Object对象作为标签,然后把textWatcher作为标签设置TAG来作为标记表明这个item的editText是否已经设置textChange监听了。在Adapter的onBindViewHolder中每次都先判断editText的Tag是否有textWatcher对象,有的话就调用removeTextChangedListener来移除由于视图复用之前绑定的textWatcher,然后就设置editText的内容setText,最后再给editText设置监听器addTextChangedListener,并把这个textWatcher加入到editText的TAG标签中。总结来说就是在适配器里先移除被复用事件,再添加新事件,如下:

//避免复用
        EditText editText =  helper.getView(R.id.et_comment);
        if (editText.getTag() instanceof TextWatcher) {
            editText.removeTextChangedListener((TextWatcher) editText.getTag());
        }
        helper.setText(R.id.et_comment, item.content);
        TextWatcher watcher = 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.content = s.length() > 0 ? s.toString() : "";
            }
        };
        editText.addTextChangedListener(watcher);
        editText.setTag(watcher);

这种方式并不优雅,而且还要进行类型判断和强制类型转型,对性能有一定的开销,下面来介绍真正的优雅解决EditText焦点混乱的解决方法:

Recycler中 多Item中EditText焦点混乱,数据错乱的正确优雅的解决方法 !

首先要知道EditText焦点错乱的问题就是RecyclerView的视图回收与复用问题,例如在位置0有item1视图中的editText已经绑定了textWatcher1监听器,但是又没有移除监听器,在位置6时又复用了item1视图,editText又再次绑定了textWatcher2监听器,此时item1视图中的editText已经绑定了2个textWatcher监听器了,当在位置6进行输入数据时,必定会导致位置0的数据变化了,令位置0的数据跟位置6的数据一模一样,也就是数据错乱了。原因是editText中的addTextChangedListener是可以绑定多个监听器的,来看看它的实现:

public void addTextChangedListener(TextWatcher watcher) {
        if (mListeners == null) {
            mListeners = new ArrayList<TextWatcher>();
        }

        mListeners.add(watcher);
    }

可以看到mListeners 是一个ArrayList数组,当多次调用addTextChangedListener时,一个editText可以绑定多个textWatcher监听器,所以要记住在每次调用addTextChangedListener完之后必须调用removeTextChangedListener来接触绑定。

那调用这两个方法的时间节点是在哪里的呢?首先我们要知道如果一个editText要获取输入值那它就首先必须要获取到焦点了,没错,答案就是设置setOnFocusChangeListener监听器来判断焦点的变化从而设置addTextChangedListener和removeTextChangedListener。

代码如下

  @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
        final ViewHolder viewHolder = (ViewHolder) holder;
        if (mList.size() <= 0) {
            return;
        }
        final EditTextBean editTextBean = mList.get(position);
        viewHolder.tv_title.setText(editTextBean.getTitle());
        viewHolder.et_content.setText(editTextBean.getContent());
        viewHolder.et_content.setHint("请填写");
        //添加EditText的监听事件
        final TextWatcher textWatcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                if (viewHolder.et_content.hasFocus()) {//判断当前EditText是否有焦点在
                    editTextBean.setContent(editable.toString());
                    //通过接口回调将数据传递到Activity中
                    mTextListener.onClickItem(position, editTextBean);
                }
            }
        };
        //设置EditText的焦点监听器判断焦点变化,当有焦点时addTextChangedListener,失去焦点时removeTextChangedListener
        viewHolder.et_content.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean hasFocus) {
                if (hasFocus){
                    viewHolder.et_content.addTextChangedListener(textWatcher);
                }else {
                    viewHolder.et_content.removeTextChangedListener(textWatcher);
                }

            }
        });
    }

通过设置setOnFocusChangeListener来监听焦点就能完美的解决了RecyclerView中EditText数据监听textWatcher导致的数据错乱问题了。目前来说是最优雅的一种方式了,也不需要用到Tag了

 Recycler中 多Item中CheckBox焦点混乱,数据错乱的解决方法:

  //避免复用
        CheckBox checkBox = helper.getView(R.id.mCheckBox);
        checkBox.setOnCheckedChangeListener(null);
        if (item.anonymous) {
            checkBox.setChecked(true);
        } else {
            checkBox.setChecked(false);
        }
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                item.anonymous = isChecked;
            }
        });

Recycler中 多Item中RadioGroup ,RadioButton焦点混乱,数据错乱的解决方法

  CertigierInfoListBean bean= mItems.get(position);
 //避免复用 
  viewHolder.radioGroup_have_partner.setOnCheckedChangeListener(null);
            String isSpouse = bean.getIsSpouse();

            if ("1".equals(isSpouse)) {//有配偶
                viewHolder.rb_yes_partner.setChecked(true);
                viewHolder.rb_no_partner.setChecked(false);
            } else {
                viewHolder.rb_yes_partner.setChecked(false);
                viewHolder.rb_no_partner.setChecked(true);
            }

            viewHolder.radioGroup_have_partner.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroup radioGroup, int id) {
                    switch (id) {
                        case R.id.rb_yes_partner_borrower://有配偶
                                bean.setIsSpouse("1");
                                if (onInfoClickedListener != null) {
                                    onInfoClickedListener.onClickItem(position, bean);

                            }

                            break;
                        case R.id.rb_no_partner://无配偶
                            bean.setIsSpouse("0");
                            if (onInfoClickedListener != null) {
                                onInfoClickedListener.onClickItem(position, bean);
                            }
                            break;
                    }

                }
            });

 

 

 

 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值