Android ListView运行效率优化
上篇介绍了ListView的用法,链接如下,这篇主要讲解如何提升ListView的效率
Android 定制ListView的界面
1、使用缓存布局
ListView如果不进行优化,其运行效率是很低的,因为在Adapter类的getView方法中,每次都将布局重新加载一遍,当ListView快速滚动时,这就会成为性能瓶颈。
Adapter类的getView方法有一个参数:View convertView,这个参数用于将之前加载好的布局进行缓存,以便之后进行重用,我们可以借助这个参数进行性能优化。
class FruitAdapter(activity: Activity, private val resourceId: Int, data: List<Fruit>): ArrayAdapter<Fruit>(activity, resourceId, data) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { Log.d("FruitAdapter", "convertView: $convertView ") val view = convertView ?: LayoutInflater.from(context).inflate(resourceId, parent, false) val imageView = view.findViewById<ImageView>(R.id.fruitImage) val fruitName: TextView = view.findViewById(R.id.fruitName) val fruit = getItem(position) if (fruit != null) { imageView.setImageResource(fruit.imageId) fruitName.text = fruit.name Log.d("FruitAdapter", "name: ${fruit.name} position: $position") } return view } }
其中标红的代码就是优化后的逻辑。如果convertView 为空,则使用LayoutInflater去加载布局,如果不为空,则直接对convertView进行重用。这样就大大提高了ListView的运行效率,在快速滚动的时候可以表现出更好的性能。
2、使用ViewHolder优化findViewById
虽然现在不会重复加载布局,但是每次在getView方法中仍然会调用View的findViewById()方法来重复获取控件的实例。这也是比较消耗性能的操作。
对此我们可以使用ViewHolder来对这部分性能进行优化。
class FruitAdapter(activity: Activity, private val resourceId: Int, data: List<Fruit>): ArrayAdapter<Fruit>(activity, resourceId, data) { inner class ViewHolder(val fruitImage: ImageView, val fruitName: TextView) override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { Log.d("FruitAdapter", "convertView: $convertView ") val view: View val viewHolder: ViewHolder if (convertView == null) { view = LayoutInflater.from(context).inflate(resourceId, parent, false) val imageView = view.findViewById<ImageView>(R.id.fruitImage) val fruitName: TextView = view.findViewById(R.id.fruitName) viewHolder = ViewHolder(imageView, fruitName) view.tag = viewHolder } else { view = convertView viewHolder = view.tag as ViewHolder } val fruit = getItem(position) if (fruit != null) { viewHolder.fruitImage.setImageResource(fruit.imageId) viewHolder.fruitName.text = fruit.name } return view } }
我们新增了一个内部类ViewHolder,用于对ImageView和TextView的控件实例进行缓存(Kotlin中使用inner class关键字来定义内部类)。
使用View的setTag()方法来对ViewHolder进行保存,这样在缓存存在的情况下,没有必要每次通过findViewById来获取控件实例了。
通过这两步优化后,ListView的运行效率就已经非常不错了。
3、ListView的layout_height的属性设置为match_parent
如果ListView的layout_height的属性不是match_parent,即高度不是动态的话,那么在FruitAdapter的getView方法会重复执行几遍;
举个例子,假设ListView能同时显示的item子项为10项,那么ListView初始化的时候,getView应该执行十次,即初始化能看见的子项数据。但是如果ListView的layout_height的属性不是match_parent,ListView可能内部要经过一些复杂的逻辑来计算当前ListView的高度应该显示几行数据,那么就会导致最终getView的执行次数为30次,这十个子项被重复渲染了三次。如果追求效率的提升,在业务场景满足的情况下,尽可能设置ListView的layout_height的属性为match_parent。
posted on 2023-06-28 17:36 Devil'soul 阅读(119) 评论(0) 编辑 收藏 举报