0%

Android开发:创建自定义视图–优化视图

翻自:http://developer.android.com/training/custom-views/optimizing-view.html 现在,你已经有一个良好设计的视图,可以响应手势,在两个状态间平滑切换.你要确保这个视图跑得快。为了避免感觉到卡,你应该确保动画能保持在每秒60帧。 少做,别太频繁 为了加速你的视图,你要消除在运行时频繁调用的不必要代码。先从onDraw()方法开始,它会给你最大的回报。特别是你应该避免在onDraw里new对象,因为内存分配会引发垃圾回收导致卡顿。应该在初始化时创建对象,或在动画间。永远不要在动画运行时创建。 除了精简onDraw(),你应该确保它尽可能地少调用。大多数onDraw()调用是调用invalidate()的结果。所以,消除不必要的invalidate()调用.如果有可能,调用四个参数的invalidate()要比没有参数的好。没参数的invalidate()会让整个View失效,四个参数 的invalidate()只会让指定的部分失效.这个方法能让绘图更高效,消除不必要的重绘.如果发现测量有冲突, 另一个成本高昂的操作是遍历布局.任何时候,一个视图调用requestLayout(),Android UI系统需要遍历整个视图层次,确定每个视图需要多大。如果发现测量有冲突,还可能需要遍历多次。UI设计者有时为了让UI正确的表现,创建了深层嵌套的ViewGroup对象,这些深层视图导致性能问题。UI层次越浅越好. 如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup去执行他的布局.不像内建的视图,你的自定义视图可以专为这个程序假定子控件的尺寸和形状.以此避免子控件的遍历计算.PieChart案例展示了如何继承ViewGroup作为自定义视图的一部分。PieChart有子视图,但是从不测量他们,而是直接通过它自己自定义布局算法设置他们的大小 使用硬件加速 从Android 3.0开始,Android 2D图形系统可以使用GPU硬件加速。对很多应用来说,开启GPU加速会有巨大的性能提升,但不是每个程序都适用。Android框架让你可以精确控制哪些部分开启硬件加速,而哪些不要。 可参考Android Developers Guide中硬件加速的部分,指引你如何加速Application,Activity,或者Window级别。注意,你必须把你的应用target API设到11以上,在AndroidManifest.xml中,加上。 一旦你开启硬件加速,你或者看不到性能的提升。移动GPU擅长于某些的任务,像缩放,旋转,转换位图。它们不擅长其他像画直线或曲线的任务。为了充分利用GPU加速,你应该让GPU擅长的操作最大化,让它不擅长的操作最小化。 在PieChart案例中,画饼是相对耗资源的,每次旋转都重绘饼导致UI卡顿。解决方案是把饼图放到一个子视图里,并设置视图的layer type为LAYER_TYPE_HARDWARE,GPU就可以把它当成一张静态图片缓存它.例子把子视图定义成了一个PieChart的内部类,这是实现这个方案所需修改代码最少的办法。

private class PieView extends View {

       public PieView(Context context) {
           super(context);
           if (!isInEditMode()) {
               setLayerType(View.LAYER_TYPE_HARDWARE, null);
           }
       }
       
       @Override
       protected void onDraw(Canvas canvas) {
           super.onDraw(canvas);

           for (Item it : mData) {
               mPiePaint.setShader(it.mShader);
               canvas.drawArc(mBounds,
                       360 - it.mEndAngle,
                       it.mEndAngle - it.mStartAngle,
                       true, mPiePaint);
           }
       }

       @Override
       protected void onSizeChanged(int w, int h, int oldw, int oldh) {
           mBounds = new RectF(0, 0, w, h);
       }

       RectF mBounds;
   }

改了这些代码后,PieChart.PieView.onDraw()只在第一次显示时调用,在application生命周期的其他时候,饼图被当做图片缓存,通过CPU重绘不同角度的旋转.GPU擅长做这些事,性能差异立即明显。 不过,有一个权衡。硬件层缓存图片会消耗显存,这个资源是有限的。因此,最终版本的 PieChart.PieView 只在用户滚动的时候设置了LAYER_TYPE_HARDWARE,其他时候,设为LAYER_TYPE_NONE,允许GPU停止缓存图片. 最后,别忘了性能分析。同样的技术,在一个视图上可以提升性能,但对另一个可能会有反效果。