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编辑  收藏  举报

导航