要求:屏幕中显示一个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); } } }
解释:
通过打印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上的内容保存至集合中,那么保存到集合的哪一个下标中呢?看这行
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中输入内容,打印数据如下:
可以看到,我只在一个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),当触发监听的时候,自然会多处响应,导致了数据显示的混乱。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。