viewAndroid安卓资料汇总

RecyclerView中解决EditText的各种异常

2017-03-18  本文已影响1219人  kaxi4it

零 前言


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

一 引言


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

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

二 效果图


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

三 解决方案


//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里的文字内容集合
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);
}
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);
}
//输入法管理类
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.setItemViewCacheSize(0);

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

四 结束语


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

上一篇下一篇

猜你喜欢

热点阅读