提升ListView的运行效率笔记
我做的 ListView 的时每个子项会显示一张图和一行文字,效果如下图:
因为要显示一张图和一段文字,直接使用 ArrayAdapter 适配器无法做到,就需要创建一个自定义适配器继承 ArrayAdapter 并重写 getView() 方法,先贴出没有进行提升效率前的自定义适配器类的代码:
public class AnimalAdapter extends ArrayAdapter{ private int resouceId; public AnimalAdapter (Context context, int textViewResouceId, List object){ super(context,textViewResouceId,object); resouceId = textViewResouceId; } @Override public View getView(int position, View converView, ViewGroup parent){ Animal animal = getItem(position); //获取当前子项的Animal实例 View view = LayoutInflater.from(getContext()).inflate(resouceId,parent,false); ImageView animalImage = (ImageView)view.findViewById(R.id.animal_image); TextView animalName = (TextView)view.findViewById(R.id.animal_name); animalImage.setImageResource(animal.getImageId()); animalName.setText(animal.getName()); return view; } }
Animal 类就是每个子项里要显示的数据类,包括一个 int 类型的图片 Id,和一个 String 类型的动物名字。
在上面那段代码中,每次 ListView 界面滚动时,都会为新进入屏幕的子项重新加载布局,没有将滚出屏幕的子项的 View 进行复用(就是 getView() 方法中的 converView 参数),并且每次都会创建新的 ImageView 和 TextView 对象来获取控件实例,这样运行效率会很低,下面分两部来进行优化:
第一步,修改 getView() 方法,复用 converView:
public View getView(int position, View converView, ViewGroup parent){ Animal animal = getItem(position); //获取当前子项的Animal实例 View view; if (converView == null){ view = LayoutInflater.from(getContext()).inflate(resouceId,parent,false); }else { view = converView; } ImageView animalImage = (ImageView)view.findViewById(R.id.animal_image); TextView animalName = (TextView)view.findViewById(R.id.animal_name); animalImage.setImageResource(animal.getImageId()); animalName.setText(animal.getName()); return view; }
可以看到,在中间加了一个if判断,判断 converView 是否为空,不为空就还是用 LayoutInflater 为子项加载我们传入的布局,因为在刚进入 ListView 界面时是没有 converView 的,需要先为整个屏幕创建足够的子项 View,当滑动时有子项滚出屏幕了才有 converView,所以当有子项滚出屏幕 converView 不为空时才能对其进行复用。
第二部,在第一步的基础上添加一个内部类 ViewHolder,用于对控件的实例进行缓存:
class ViewHolder{ ImageView animalImage; TextView animalName; }
在 getView 中,当 converView 为空时实例化一个新的 ViewHolder 对象,将控件的实例都存放在 ViewHolder 中,然后调用 view 的 setTag() 方法将 ViewHolder 对象储存在 view 中,那样,当重用 converView 时就可以用 getTag() 方法将 ViewHolder 重新取出来直接设置新的要显示的 Animal 实例信息就可以了,不需要再重新绑定获取控件实例了。
public View getView(int position, View converView, ViewGroup parent){ Animal animal = getItem(position); //获取当前子项的Animal实例 View view; ViewHolder viewHolder; //当converView不为空时,则对converView进行回收重用,提高运行效率 if (converView == null){ view = LayoutInflater.from(getContext()).inflate(resouceId,parent,false); viewHolder = new ViewHolder(); viewHolder.animalImage = (ImageView)view.findViewById(R.id.animal_image); viewHolder.animalName = (TextView) view.findViewById(R.id.animal_name); view.setTag(viewHolder); }else { view = converView; viewHolder = (ViewHolder) view.getTag(); } viewHolder.animalImage.setImageResource(animal.getImageId()); viewHolder.animalName.setText(animal.getName()); return view; }
经过上面两部是优化后,就可以提升 ListView 的运行效率了。
代码和优化方法都是参考《第一行代码:Android》,这里仅作学习笔记加深理解。