Android RecyclerView 中多Item的EditText焦点混乱,数据错乱的解决方法(更新)

最近在做毕业设计,使用了RecyclerView来做了以下个人资料的编辑界面。RecyclerView中包含了多种不同的Item,例如图片里面的 姓名,公司,部门,职位,邮箱的Item 里面都包含一个EditText。
这里写图片描述

RecyclerView中EditText的监听处理

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

public interface onTextChangeListener {
    void onTextChanged(int pos,String str);
}

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

public class EditListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private Context mContext;
    private LayoutInflater mInflater;
    private ArrayList<EditListBean> mList;
    private onTextChangeListener mTextListener;

    public EditListAdapter(Context context, ArrayList<EditListBean> list){
        this.mContext=context;
        mInflater=LayoutInflater.from(mContext);
        this.mList=list;
    }
    //设置自定义接口成员变量
    public void setOnTextChangeListener(onTextChangeListener onTextChangeListener){
        this.mTextListener=onTextChangeListener;
    }
    //创建ViewHolder
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerView.ViewHolder holder=new     
        itemHolder2(mInflater.inflate(R.layout.edit_recyclerview_item2,parent,false));
        return holder;
    }
    //绑定ViewHolder
    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int
    position) { 
    final itemHolder2 holder2= (itemHolder2) holder;
    holder2.title.setText(mList.get(position).getKey());
    holder2.input.setText((String) mList.get(position).getValue());
    holder2.input.setHint(mList.get(position).getHint());
    //添加EditText的监听事件
    holder2.input.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) {
    //通过接口回调将数据传递到Activity中
    mTextListener.onTextChanged(position,holder2.input.getText().toString());
     }
     });

    //内部类 ViewHolder
    class itemHolder2 extends RecyclerView.ViewHolder{
        TextView title;
        EditText input;

        public itemHolder2(View itemView) {
            super(itemView);
            title= (TextView) itemView.findViewById(R.id.EditActivity_item2_title);
            input= (EditText) itemView.findViewById(R.id.EditActivity_item2_text);
        }
    }

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

    @Override
    public int getItemViewType(int position){
        return mList.get(position).getType();
    }
    }

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

mAdapter.setOnTextChangeListener(new onTextChangeListener() {
            @Override
            public void onTextChanged(int pos, String str) {
            //在这里拿到RecyclerView中Item的position和EditText中的变化
                mPedit.setList(pos,str);
                Log.e(String.format("%d is ---->",pos),str);
            }
        });

OK 上面的监听事件处理完了 我们来试试效果怎么样。首先点击姓名里面的EditText,在马化腾后面输入哈哈哈哈 然后收起键盘, 可以发现竟然下面几个Item的EditText也触发了回调事件了,也就是我编辑了Item2的EditText,然后Item7 , 9 , 11的EditText也触发了onTextChanged的监听事件了。如图,出现了监听事件错乱的重大的Bug。
这里写图片描述

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

2017年12月11号更新下,现在很多网上的解决方法是使用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标签中。总结来说就是在适配器里先移除被复用事件,再添加新事件。

例如这种方式:http://m.blog.csdn.net/jhear/article/details/75191997

 //避免复用
        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。

代码如下:

                final itemHolder2 holder2= (itemHolder2) holder;
                //holder2.input为EditTextView
                holder2.input.setText((String) mList.get(position).getValue());
                holder2.input.setHint(mList.get(position).getHint());

                final TextWatcher textWatcher=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) {
                        Log.e("textWatcher",holder.getAdapterPosition()+"");
                        if(holder2.input.hasFocus()){//判断当前EditText是否有焦点在
                            //通过接口回调将数据传递到Activity中
                            mTextListener.onTextChanged(position,holder2.input.getText().toString());
                        }
                    }
                };
                //设置EditText的焦点监听器判断焦点变化,当有焦点时addTextChangedListener,失去焦点时removeTextChangedListener
                holder2.input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
                    @Override
                    public void onFocusChange(View v, boolean hasFocus) {
                        if(hasFocus){
                            holder2.input.addTextChangedListener(textWatcher);
                            Log.e("addTextChanged",position+"");
                        }else{
                            holder2.input.removeTextChangedListener(textWatcher);
                            Log.e("removeTextChanged",position+"");
                        }
                    }
                });

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值