解决ListView中包含EditText数据混乱原理
要求:屏幕中显示一个listview,其中每一个item都有一个editText,在任一editText上输入内容,快速上下滑动,保证数据不混乱。
这是一道面试题,初看没什么,应该会很简单,但实际解决起来没那么简单,先上解决代码。
packagecom.zhiren.mytestok;importandroid.content.Context;importandroid.text.Editable;importandroid.text.TextUtils;importandroid.text.TextWatcher;importandroid.util.Log;importandroid.view.View;importandroid.view.ViewGroup;importandroid.widget.BaseAdapter;importandroid.widget.EditText;importjava.util.List;/***CreatedbyAdministratoron2017/2/20.*/publicclassMyAdaptersextendsBaseAdapter{privateContextcontext;//privateString[]str;privateListlist;publicMyAdapters(Contextcontext,Listlist){this.context=context;this.list=list;}@OverridepublicintgetCount(){returnlist.size();}@OverridepublicObjectgetItem(intposition){returnnull;}@OverridepubliclonggetItemId(intposition){returnposition;}@OverridepublicViewgetView(finalintposition,ViewconvertView,ViewGroupparent){finalViewHolderviewHolder;if(convertView==null){convertView=View.inflate(context,R.layout.item_mian,null);viewHolder=newViewHolder(convertView);convertView.setTag(viewHolder);}else{viewHolder=(ViewHolder)convertView.getTag();}Beanbean=(Bean)list.get(position);Log.e("TAG",viewHolder.text+":"+position);viewHolder.text.setTag(position);viewHolder.text.clearFocus();viewHolder.text.addTextChangedListener(newTextWatcher(){@OverridepublicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter){}@OverridepublicvoidonTextChanged(CharSequences,intstart,intbefore,intcount){intpos=(int)viewHolder.text.getTag();Beanb=(Bean)list.get(pos);b.setName(s+"");}@OverridepublicvoidafterTextChanged(Editables){}});if(!TextUtils.isEmpty(bean.getName())){viewHolder.text.setText(bean.getName());}else{viewHolder.text.setText("");}returnconvertView;}publicclassViewHolder{privateEditTexttext;publicViewHolder(Viewv){text=(EditText)v.findViewById(R.id.et_item);}}}
解释:
通过打印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进行绑定,如图:
那么这么做的意义是什么呢,可以看到item中的editText有一个监听事件:每当editText内容变化的时候都会将editText上的内容保存至集合中,那么保存到集合的哪一个下标中呢?看这行
intpos=(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(newTextWatcher(){@OverridepublicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter){}@OverridepublicvoidonTextChanged(CharSequences,intstart,intbefore,intcount){//intpos=(int)viewHolder.text.getTag();//Beanb=(Bean)list.get(pos);//b.setName(s+"");Beanb=(Bean)list.get(position);b.setName(s+"");Log.e("TAG",""+position);}@OverridepublicvoidafterTextChanged(Editables){}});
运行app后,首先上下滑动listView到最后再滑动到开头(我设置了长度为51),让每一个item充分复用,此时再次在下标为0的item中输入内容,打印数据如下:
可以看到,我只在一个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),当触发监听的时候,自然会多处响应,导致了数据显示的混乱。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。