列表滑动性能优化是一个老生常谈的问题,最近在做项目的时候又遇到了列表滑动卡顿的问题,我在经过多次思考和尝试后,终于找到了滑动卡顿的元凶,于是将经验总结下来。

ViewHolder

先说说最常规的ViewHolder。ViewHolder的出现是为了解决在绑定视图数据的时候使用findViewById遍历视图树(以深度优先的方式)查找视图引起耗时操作的问题,将第一次查找到的视图放入静态的ViewHolder中,以后绑定新数据时直接从ViewHolder中拿到视图引用。在ListView中使用ViewHolder是基本的优化思路,当然最好的是直接使用RecyclerView,它自带了ViewHolder。(推荐使用MultiType作为RecyclerView的Adapter,这个库设计的很优美)

数据处理

很多时候从我们要对服务器上获取下来的列表数据进行一次二次加工,以便转化为我们界面上要显示的数据,这些操作可能会比较耗时。比如字符串拼接、时间格式化等操作都是比较耗时的操作。比较好的实践是将列表数据的加工在notifyDataSetChanged()之前在后台线程做好,在Adapter中只做数据的绑定。

Item View 的变化

有时候我们会根据数据类型来控制一个View的显隐,当给一个View设置setVisibility(View.GONE)的时候,会触发布局的重新测量、布局、绘制等操作,若itemView的布局比较复杂,重新测量绘制会很耗时间,引起列表卡顿。这个时候可以将数据和itemView分解成不同的类型,根据类型来绑定对应的itemView,减少布局的重绘操作。

重用OnClickListener

通常我们为Button设置点击事件的时候都是直接创建一个匿名内部类的对象(new OnClickListener{}),习惯了这种绑定事件的方式后我们可能在列表中也这么做。在列表滑动的时候会不停的重复创建新的OnClickListener的操作,旧的OnClickListener会被标记为需要垃圾回收,当需要回收的对象过多的时候会引起GC,导致列表卡顿。可以创建一个通用的OnClickListener,把数据放入Button的Tag中,根据id来判断是哪个Button执行了点击,来取出数据、执行不同的逻辑。

图片的异步加载

用Glide、Picasso、Fresco等图片异步加载框架。

界面优化

优化布局层级减少过渡绘制、优化布局方式提高布局效率。关于这点官方推荐使用ConstraintLayout,在我目前的实践中,ConstraintLayout作为RecyclerView的Item布局的时候可能会有卡顿现象(主要是布局重绘引起的)。