解决ListView中包含EditText数据混乱原理

要求:屏幕中显示一个listview,其中每一个item都有一个editText,在任一editText上输入内容,快速上下滑动,保证数据不混乱。

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都木包装箱小微创业公司专业提供企业网站建设营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

这是一道面试题,初看没什么,应该会很简单,但实际解决起来没那么简单,先上解决代码。

package com.zhiren.mytestok;

import android.content.Context;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;

import java.util.List;

/**
 * Created by Administrator on 2017/2/20.
 */

public class MyAdapters extends BaseAdapter {
    private Context context;
    //    private String[] str;
    private List list;

    public MyAdapters(Context context, List list) {
        this.context = context;
        this.list = list;
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            convertView = View.inflate(context, R.layout.item_mian, null);
            viewHolder = new ViewHolder(convertView);


            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        Bean bean = (Bean) list.get(position);
        Log.e("TAG", viewHolder.text + ":" + position);
        viewHolder.text.setTag(position);
        viewHolder.text.clearFocus();


        viewHolder.text.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) {
                int pos = (int) viewHolder.text.getTag();
                Bean b  = (Bean) list.get(pos);
                b.setName(s + "");
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        if (!TextUtils.isEmpty(bean.getName())) {
            viewHolder.text.setText(bean.getName());
        } else {
            viewHolder.text.setText("");
        }

        return convertView;
    }

    public class ViewHolder {
        private EditText text;

        public ViewHolder(View v) {
            text = (EditText) v.findViewById(R.id.et_item);
        }
    }
}

解释:解决ListView中包含EditText数据混乱原理

通过打印log可以看到,屏幕中最多可以显示7行item,下标依次为:0、1、2、3、4、5、6.

其中下标为0的item的地址值是3098e6e5

通过向上滑动,当出现第8行item(下标为7)的时候,下标为0的item已经完全看不到了,根据谷歌设计原理,下标为0的item复用到了刚刚出现的下标为7的item上,其中可以看到下标为7的item的地址值也是3098e6e5,以上可以证明。这些都是大家知道的,重点看下面:

为什么要写

viewHolder.text.setTag(position);

这一行意义重大,在滑动的过程中,动态的将item与position进行绑定,如图:

解决ListView中包含EditText数据混乱原理

那么这么做的意义是什么呢,可以看到item中的editText有一个监听事件:每当editText内容变化的时候都会将editText上的内容保存至集合中,那么保存到集合的哪一个下标中呢?看这行

int pos = (int) viewHolder.text.getTag();

被点击的那行item根据getTag()获取了最近与它绑定的那个position,还以地址为3098e6e5的item为例,那么pos的值此时应该是0还是7呢?这与当前listview滑动的位置有关,如果当前屏幕能看到下标为7的item,那么此时pos就必定为7,不可能为0,第一:item只能动态与一个position相绑定,第二,绑定是动态变化的,当前屏幕能看到的是下标为7的item,自然item与下标7绑定就不能再与下标0绑定了。那么就得到pos为7,集合就会将当前editText的内容保存到下标为7的对象中。那么无论再怎么上下滑动,只有当positon为7的时候才能从集合中获取那条保存的数据,其他position都不可以,其他item也同理。

如果正常写会出现什么呢?

例如:监听器里不getTag()

viewHolder.text.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) {
//                int pos = (int) viewHolder.text.getTag();
//                Bean b  = (Bean) list.get(pos);
//                b.setName(s + "");
                Bean b = (Bean) list.get(position);
                b.setName(s + "");
                Log.e("TAG", "" + position);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

运行app后,首先上下滑动listView到最后再滑动到开头(我设置了长度为51),让每一个item充分复用,此时再次在下标为0的item中输入内容,打印数据如下:

解决ListView中包含EditText数据混乱原理

可以看到,我只在一个item的editText中输入了1个字,按道理来说应该只触发下标为0的那个item,并将下标为0的item上的数据保存到集合,但事实上却触发了这么多item中的editText的监听,这是为什么呢?

原因是在上下快速滑动的过程中,下标为0的item出现了大量的复用情况,例如第0、7、14、21行的item都复用了这一个item,而适配器中的getView()方法经过了多次的执行,每次执行完毕后一些无用的资源就被回收了,但是item的editText是保存在viewHolder中的,并没有被回收,但是多次的执行getView()方法,每一次都让Item中的这个editText在对应的positon下设置了一次监听,那么多次设置监听,对应的是不同的位置(position),当触发监听的时候,自然会多处响应,导致了数据显示的混乱。


当前标题:解决ListView中包含EditText数据混乱原理
分享URL:http://ybzwz.com/article/jghsee.html