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

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

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

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);
        }
    }
}


解释:wKiom1ir0-HRh-Q9AACx4z3Czt8488.jpg-wh_50

通过打印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进行绑定,如图:

wKioL1ir1taizi86AABKX-swbb4328.png-wh_50

那么这么做的意义是什么呢,可以看到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中输入内容,打印数据如下:

wKiom1ir2-TzjSiTAADBY-sl4YQ323.jpg-wh_50

可以看到,我只在一个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),当触发监听的时候,自然会多处响应,导致了数据显示的混乱。

本文出自 “移动平台开发” 博客,请务必保留此出处http://liuxudong1001.blog.51cto.com/10877072/1899826

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值