美文网首页思考与总结Android
EditText在RecyclerView中的解决方案

EditText在RecyclerView中的解决方案

作者: 星风雪雨 | 来源:发表于2018-12-16 15:15 被阅读54次

    有时候,一个列表中的Item会有EditText的出现,而由于View复用机制,如果不好好处理EditText,将会出现一些问题。之前做项目中也遇到了这个问题,通过摸索以及思考,最终得到了解决方案。

    其实有些问题的出现,还是由于没有理解RecyclerView的复用机制和EditText,主要原因还是菜,哈哈。

    菜是原罪

    EditText在RecyclerView中的问题

    例子是这样的,每个Item包含一个title、一张图片以及一个评分,这个评分就是通过输入框来输入的。

    问题1——复用机制、未绑定数据导致的

    先看下第一段Adapter里面的逻辑:

    class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
        var ivPic: ImageView = itemView.findViewById(R.id.ivPic)
        var etScore: EditText = itemView.findViewById(R.id.etScore)
        var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
    
        fun updateView(picItem: PicItem) {
            ivPic.setImageResource(picItem.picResId)
            tvTitle.text = picItem.title
            if (picItem.score == null) etScore.hint = "请输入分数" else etScore.setText(picItem.score)
        }
    }
    

    上面是ViewHolder的代码,Adapter的onBindViewHolder就是调用了updateView()方法,这里面暂时还没把输入框的内容和PicItem绑定。先看下效果如下:

    EditText未绑定数据的情形

    这里,每张图片输入图片title对应的分数,可以看到,由于未绑定数据和RecyclerView的复用机制的存在,在一些图片中还没输入分数,就已经出现分数了。那下面先来进行数据的绑定。

    问题2——错误的绑定机制

    要想在EditText输入后绑定数据,怎么搞?TextWatcher了解一下,操作方式如下:

    class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
        var ivPic: ImageView = itemView.findViewById(R.id.ivPic)
        var etScore: EditText = itemView.findViewById(R.id.etScore)
        var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
        var myTextWatcher: MyTextWatcher=MyTextWatcher()
    
        fun updateView(picItem: PicItem) {
            ivPic.setImageResource(picItem.picResId)
            tvTitle.text = picItem.title
            if (picItem.score == null) etScore.hint = "请输入分数" else etScore.setText(picItem.score)
            myTextWatcher.picItem=picItem
            etScore.addTextChangedListener(myTextWatcher)
        }
    }
    
    class MyTextWatcher: TextWatcher {
    
        lateinit var picItem:PicItem
    
        override fun afterTextChanged(s: Editable?) {
            picItem?.apply {
                score=s?.toString()
            }
        }
    
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }
    
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }
    }
    

    效果如下:

    TextWatcher的不正确使用

    可以发现,还是乱的。想了想,这是咋回事呢?原来是因为这里是addTextWatcher,而不是setTextWatcher,也就是在复用的时候,同一个EditText添加了多个TextWatcher,怪不得分数9还能出现在上面了。

    想清了原因,那就好办了。 首先我是试了一个,removeTextWatcher的方法,那就是在Adapter的detachViewHolderFromWindow方法中移除TextWatcher,如下:

    class PicAdapter(val picItems: List<PicItem>) : RecyclerView.Adapter<PicViewHolder>() {
        override fun onViewDetachedFromWindow(holder: PicViewHolder) {
            super.onViewDetachedFromWindow(holder)
            holder.etScore.removeTextChangedListener(holder.myTextWatcher)
        }
    }
    

    实践发现,依赖onViewDetachedFromWindow是不靠谱的。

    解决方案

    经过思考,由于RecyclerView的复用机制,导致了以下关系的存在:

    一个ViewHolder——>一个EditText——>多个TextWatcher——>多个PicItem

    这里我们可以将多个TextWatcher始终绑定一个,那就需要在ViewHolder的初始化里面操作,而不是在updateView,因为会多次bind,这就得到了以下关系:

    一个ViewHolder——>一个EditText——>一个TextWatcher——>多个PicItem

    那么也就是说TextWatcher负责多个PicItem的更新,怎么做呢?

    很简单,在updateView(),也就是bind过程中每次去更新PicItem就可以了。最终代码如下:

    class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
        var ivPic: ImageView = itemView.findViewById(R.id.ivPic)
        var etScore: EditText = itemView.findViewById(R.id.etScore)
        var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
        var myTextWatcher: MyTextWatcher = MyTextWatcher()
    
        init {
            etScore.addTextChangedListener(myTextWatcher)
        }
    
        fun updateView(picItem: PicItem) {
            myTextWatcher.picItem = picItem
            ivPic.setImageResource(picItem.picResId)
            tvTitle.text = picItem.title
            if (picItem.score == null) {
                etScore.hint = "请输入分数"
                etScore.setText("")
            } else {
                etScore.setText(picItem.score)
            }
        }
    }
    

    最终效果如下:

    正确效果

    模拟器滑动起来感觉有点便秘,将就看着吧。。。。

    另外如果对这个美女感兴趣,她叫刘芸,不要谢我,去百度搜图片去吧。。。😆😀😀

    总结

    其实后来想想,如果能明白RecyclerView复用机制,EditText的TextWatcher机制,其实很容易解决这种问题,那么绕路了的原因就是因为。哎,不多说了,学习去了。。

    代码地址:https://github.com/wangli135/ClimbDemo/tree/master/app

    相关文章

      网友评论

        本文标题:EditText在RecyclerView中的解决方案

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