美文网首页viewAndroid安卓资料汇总
RecyclerView中解决EditText的各种异常

RecyclerView中解决EditText的各种异常

作者: kaxi4it | 来源:发表于2017-03-18 12:29 被阅读1219次

    零 前言


    此文发现BUG,换了思路,重写了一篇并更新了DEMO代码 地址如下:http://www.jianshu.com/p/1c91bdc96d16
    可以直接忽略下面全文

    一 引言


    闲着无聊看到挺多群内都有人在咨询listview中的edittext焦点错乱,数据错乱等问题,然后顺手也搜索了一下网上的解决方案和部分demo,发现总有不尽如人意的地方和一些作者也没有发现的bug。
    然后在同事的怂恿下,也尝试着提供一个靠谱的解决方案。
    但是现在已经是2017年了,列表控件再用listview的话,有点活在过去的感觉啊,所以与时俱进的选择了RecyclerView来代替ListView,经过N个小时的研究与整理,把几个问题点都过滤掉了,留下此DEMO,赠与有缘人。

    特别感谢:苏泽兄、逗你玩222对文章的错漏之处提出的改正意见

    二 效果图


    为了节约大家宝贵的流量,就不放图了

    三 解决方案


    • 解析整个问题点之前,先把项目的完整demo放送给大家,地址如下:
      https://github.com/kaxi4it/EditTextInRecyclerViewDemo
      注:阅读以下文章时,建议对照demo代码对比观看
    • 焦点错乱
      editText的焦点,我们可以通过一个int变量记录他在adapter中的位置
    //edittext的焦点位置
    int etFocusPos = -1;
    

    然后在onBindViewHolder方法中,通过比较edittext的焦点位置 与 viewholder的position 来判断处理,是否需要让item中的edittext获取焦点,并把光标位置置于输入框内文字的最后一位。

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
            //当前holder的position
            final int position = i;
            ...
            //当前holder是我们记录下的焦点位置时,我们给当前的editext设置焦点并设置光标位置
            if (etFocusPos == position) {
                    viewHolder.et.requestFocus();
                    viewHolder.et.setSelection(viewHolder.et.getText().length());
            }
            ...
            //我们给当前holder中的edittext添加touch事件监听,在action_up手指抬起时,记录下焦点position
            viewHolder.et.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                            if (event.getAction() == MotionEvent.ACTION_UP) {
                                    etFocusPos = position;
                            }
                            return false;
                    }
            });
    }
    

    接着,因为是在列表中使用了EditText,所以软键盘输入时,会有下一项,下一行的提示帮助用户快速切换下一个输入框,这里就需要对EditText添加OnFocusChangeListener监听,来保障焦点位置的正确监听和记录

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
        final int position = i;
        final ItemHolder viewHolder = (ItemHolder) holder;
        viewHolder.et.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View view, boolean b) {
                if (b){
                    etFocusPos = position;
                }
            }
        });
    }
    
    • 数据错乱
      列表滑动后,EditText内输入的内容错乱问题,也比较好解决,就是把每次输入的文本内容,相应的保存下来,再显示的时候,赋值即可,adapter里的关键代码如下。
    //edittext里的文字内容集合
    SparseArray<String> etTextAry = new SparseArray();
    //监听文字变化的TextWatcher接口
    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) {
                    //保存输入的文字内容
                    etTextAry.put(etFocusPos, s.toString());
            }
    };
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) {
            //当前holder的position
            final int position = i;
            final ItemHolder viewHolder = (ItemHolder) holder;
            ...
            //根据对应的位置填入对应的输入框记录内容
            viewHolder.et.setText(etTextAry.get(position));
            ...
            //给EditText添加监听
            viewHolder.et.addTextChangedListener(textWatcher);
    }
    
    • 监听错乱
      因为RecyclerView的复用功能,所以显示给你的EditText也许是从cache里提供给你的,那么这个EditText内其实已经绑定过TextWatcher的实现类,我们翻阅TextView(addTextChangedListener是TextView的代码,EditText是继承TextView所实现的)的代码可以发现
    public void addTextChangedListener(TextWatcher watcher) {
            if (mListeners == null) {
                mListeners = new ArrayList<TextWatcher>();
            }
    
            mListeners.add(watcher);
        }
    

    源码中的TextWatcher是被add进一个Listener集合中,所以当我们复用ViewHolder时,会无意识的导致添加了N个TextWatcher在当前EditText,所以导致了监听事件的错乱,那么我们只需要在ViewHolder被销毁时把之前添加进集合的TextWatcher从集合中remove掉那么就能保证每个EditText只添加一个TextWatcher监听器

    @Override
    public void onViewRecycled(RecyclerView.ViewHolder holder) {
            super.onViewRecycled(holder);
            //当前holder被销毁时,把holder的TextChangedListener删除
            ItemHolder viewHolder = (ItemHolder) holder;
            viewHolder.et.removeTextChangedListener(textWatcher);
    }
    
    • 输入法隐藏
      举个栗子,当我们在EditText中设置了inputType为number后,会发现焦点滑出后,软键盘或者还在显示,或者关闭了数字键盘,但是英文键盘还显示着,等等系列问题,经过几次试错,发现使用如下方法就可以解决这个问题
    //输入法管理类
    InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    @Override
    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {
            super.onViewDetachedFromWindow(holder);
            //当获取焦点的editText从window隐藏时
            if (etFocusPos == holder.getAdapterPosition()) {
                    inputMethodManager.hideSoftInputFromWindow(((ItemHolder) holder).et.getWindowToken(), 0);
            }
    }
    
    • 其他错乱
      虽然以上方案解决了大部分问题,但是最后由于RecyclerView的缓存机制,所以默认情况下,他会保留最后分离出来的2个ViewHolder,那么在这种机制下就会导致我们的onBindViewHolder方法并不会100%的在显示ViewHolder时调用,因为总是会有2个ViewHolder是从mCachedViews的集合中读取而来,导致我们在onBindViewHolder中的一些方法并没有被再次执行到。
      比如该DEMO中关于EditText的焦点绑定事件,就会因为该机制的原因,会在几率出现已记录的焦点绑定失败的情况,那么我们可以选择把默认的mCachedViews集合数设置为0,那么就能保证每一次ViewHolder的显示都需要经过onBindViewHolder方法。
    recyclerView.setItemViewCacheSize(0);
    

    除非有类似该DEMO的特例需求,其他正常情况下并不推荐如此操作,因为缓存是个好东西,只是偶尔有点小尴尬罢了(__)

    四 结束语


    希望以上的完整DEMO和代码的讲解能帮您解决这些小问题,最后无耻的广告一下
    最近正在发展副业,代理了一款《Divoom TimeBox 2代智能蓝牙音箱》的小产品,如果您看了以后,觉得"唉?这小玩意不错哎,有点意思,送礼挺合适的",那么欢迎前来选购,悄悄和客服说下您是简书来的读者,我会帮您改价,在原价的基础上降价100元,如果您也想发展个副业,做个小代理,那么欢迎前来私信
    https://item.taobao.com/item.htm?id=545135458341

    相关文章

      网友评论

      • 14e302696882:楼主 我有一个页面有两个recycleview,然后用了NestedScrollView套在外面,两个recycleview的每一项都有edittext,当我点击某一个edittext,软键盘弹出,整个
        NestedScrollView网上滑动,但是软键盘上面的那一项不是我刚刚点的获取了焦点的
        那一项,这个问题也不知道什么情况 楼主帮忙看看
        kaxi4it:@宇宙星辰老祖 有尝试过adjustpan吗?
      • 逗你玩222:楼主你好,正常输入好像没问题。。。如果我一个edittext输入后,后面都按换行后输入数字,上下滑动都出问题了。。。。这个楼主你可以看看!
        kaxi4it:已经找到问题并解决,一会会同步更新文章与github的,再次感谢
        kaxi4it:@逗你玩222 好的 十分感谢,我一会看一下
      • 逗你玩222:楼主你这demo明明edittext输入内容,滑动下就没有了
        kaxi4it:可是,我刚又跑了一次,并没有你说的问题哎,能详细说明情况吗?

      本文标题:RecyclerView中解决EditText的各种异常

      本文链接:https://www.haomeiwen.com/subject/drwjnttx.html