我们使用ViewHolder时,把每一个item的子View控件对象都放在ViewHolder中,当第一次创建convertView对象时,便把这些item的子View控件对象findViewById实例化出来并保存到ViewHolder的对象中。然后用convertView的setTag将viewHolder对象设置到Tag中, 当以后加载ListView的item时便可以直接从Tag中取出复用ViewHolder对象中的,不需要再findViewById找item的子控件对象了。这样便大大提高了性能。
但是,某些情况下,这种优化技术便出现了一种问题,举个例子,当我们由于项目需要,需要在ListView中用到CheckBox时,那么,就会出现这样的情况:
假设一个ListView有10个Item,当我选择第一个Item的CheckBox之后,往下滑动到原先不可见的位置时就会发现,还有其他的CheckBox也被选中了,这样就造成了很不好的用户体验。
先上出现了这种问题的代码:
布局文件:
activity_listview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</LinearLayout>
item_list.xml 一个CheckBox和一个TextView,设为100dp高的原因是,必须有未显示的Item,ListView才会去复用View,如果一个屏幕显示了全部的Item,那么就不回复用了。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp">
<CheckBox
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/textView"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</LinearLayout>
MyAdapter.java
package cn.zmit.myapplication;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
/**
* Created by Administrator on 2016/3/23.
*/
public class MyAdapter extends BaseAdapter {
private List<String> list;
private Context context;
public MyAdapter(List<String> lists, Context contexts) {
list = lists;
context = contexts;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(context, R.layout.item_list, null);
holder.textView = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(list.get(position));
return convertView;
}
private class ViewHolder {
TextView textView;
}
}
ListVeiwDemo.java
package cn.zmit.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2016/3/18.
*/
public class ListViewDemo extends Activity {
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_listview);
listView= (ListView) findViewById(R.id.listview);
List<String> list=new ArrayList<>();
for(int i=0;i<10;i++){
list.add(i+"");
}
MyAdapter adapter=new MyAdapter(list,this);
listView.setAdapter(adapter);
}
}
非常正常的一个ListView实现过程,贴代码的原因是担心有初学者不懂我在说什么。大家可以把代码拷贝一下或者自己编写看一下效果。
接下来说一下怎么解决!!!!
首先,定义一个Map,
Map<Integer,View>map=new HashMap<>();
当convertView为null时,在convertView和子控件加载完成后,将convertView加入map:
map.put(position,convertView);
我们想一想,这时候,在前面判断的时候,还应该是
if (convertView == null)
这样吗?当然不是,换成这样:
if (map.get(position)==null)
也就是说,只要你这个Item没被加载过,就给我重新加载!
然后在下面的else里这样写:
else {
convertView=map.get(position);
holder = (ViewHolder) convertView.getTag();
}
假设map里有这个View,就拿出来用。
全部解决完问题的Adapter的getView部分代码为:
Map<Integer,View>map=new HashMap<>();
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (map.get(position)==null) {
holder = new ViewHolder();
convertView = View.inflate(context, R.layout.item_list, null);
holder.textView = (TextView) convertView.findViewById(R.id.textView);
map.put(position,convertView);
convertView.setTag(holder);
} else {
convertView=map.get(position);
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(list.get(position));
return convertView;
}