Android系统下2D绘图性能提升的一些方法

作者 Zhenpu.Zhang


笔者近期开发了一款绘图类应用,其中在绘图性能提升效率方面遇到一些问题,经过咨询高手和查找前人经验,积攒了一些小小的经验,特地写下,希望能给同样有需要的程序猿一些启发。


下面进入正题,首先介绍一些基本概念;

Android绘画需要的一些基本元素

1.Canvas和Bitmap

其实在自定义的View中,我们就已经使用到了Canvas,在重写的onDraw(Canvas canvas)方法中就有一个Canvas对象,它负责对当前的View进行绘制。当然在绘画方面,它拥有更多的方法,去帮助我们去画线、路径、矩形、图片等。Bitmap类中存储了屏幕上具体的像素值,所以我们常用它来存储绘画的数据,Bitmap又可以写入到.png,.jpg这样图片文件中。Bitmap还可以用来构造Canvas对象。

Bitmap的常用法:

Bitmap mBitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);//构造方法

mBitmap.recycle();//解除本地对象对该Bitmap对象的引用,清除对像素数据的引用。

Canvas的构造方法:

CanvasmCanvas = new Canvas();

CanvasmTempCanvas = new Canvas(mBitmap);//用Bitmap对象构造Canvas对象

Canvas常用的绘画方法:

drawColor(intcolor);//绘制canvas的背景颜色

drawPath(Pathpath,Paint paint);//绘制路径

drawLine(floatstartX, float startY, float stopX, float stopY, Paint paint);//绘制直线

drawCircle(floatcx, float cy, float radius, Paint paint);//绘制圆形

drawArc(floatleft, float top, float right, float bottom, float startAngle, float sweepAngle,Boolean userCenter, Paint paint)//绘制圆弧

drawOval(floatleft, float top, float right, float bottom, Paint paint);//绘制椭圆

drawRect(RectFrect,Paint paint);//绘制矩形

drawBitmap(Bitmapbitmap, float left, float right, Paint paint);//绘制图片或者加载图片

drawText(String text, float x, float y, Paintpaint);//渲染文字

2.Path

Path即路径,我们可以用它来记录用户在屏幕上移动的轨迹,它具有比较流畅的线性,不仅包括直线还有二次和三次曲线,能够较好好地处理拐角。我们在构造了一个path后,给它起点,然后用它的画线方法lineTo(float x, float y),就可以利用给定的坐标点,更新当前的path。然后利用上面的drawPath()方法就可以把它画到canvas上面。

Path 的常用方法:

Path mPath = new Path();//构造一个Path对象

mPath.moveTo(float x, float y);//设置Path的起点

mPath.lineTo(float x, float y);//把(x, y)作为终点加入Path,更新Path

PathMeasure pathMeasure = newPathMeasure(mPath, false);

pathMeasure.getLength();//获取当前Path的长度

3.Paint

在前面Canvas的绘画方法中,我们可以看到Paint对象也是必不可少的,原因是我们在绘画时需要指定画笔的相关属性,如颜色,线宽,荧光效果,擦除效果等。有了不同属性的画笔,我们才能画出更丰富的画面。

Paint 常用的方法:

setColor(int color);//设置画笔颜色

setStrokeWidth(int width);//设置线宽

setAntiAlias(boolean bool);//设置抗锯齿

setARGB(int a, int r, int g, int b);//设置透明度,颜色

setXfermode(null);//普通画笔

setXfermode(newPorterDuffXfermode(PorterDuff.Mode.CLEAR));//擦除模式

setMaskFilter(BlurMaskFilter mBlurMaskFilter);//边缘模糊

有了上面的这些元素,那么我们就可以在一个View上面滑动手指,然后用Path变量去保存手指移动的轨迹并根据touchEvent的坐标点去更新Path,用一个Paint对象设置画笔的属性,再用一个Bitmap构造一个Canvas对象,把Path实时地画到Canvas上面,最后把Bitmap保存成图片,这样就实现了基本的绘画功能。但是考虑到硬件设备的各种限制,我们还需要设法去提高性能。

提高绘画效率的方法

1.控制Path的长度

Path的长度对绘画时间影响是比较大的,这个我们可以通过观察不同长度的Path调用drawPath()画到Canvas上的执行时间来验证,那为什么不把原来很长的Path进行截断呢?如果每次都画很短的一小段Path,那么就很高效了。做法很简单,就是每次在Path加入新的坐标点后,就判断一下它的长度,前面的PathMeasure类提供了测量Path长度的方法,如果Path长度大于一定的值(如100),就让Path的起点重新设置为该点,继续更新和绘制。经过这么一改,那么就算绘制了很长的线条,那么它的绘制速度始终如初。

2.利用临时的Canvas和Bitmap

然而上述方法只能处理一笔的绘制慢的问题,当我们绘制了几百笔甚至几千笔,那么问题又来了,因为每次都是从第一笔画到最后一笔,这样绘画的负担就很重,这时还可以引入临时的Bitmap和Canvas,把最近正在画得这些笔画绘制到临时的tempCanvas上面,把以前绘制的笔画画到结果resultCanvas上面,最后把tempBitmap画到结果resultCanvas上面。需要注意的是tempCanvas需要进行清除(绘制Path之前先进行一次clear,具体做法是tempCanvas.drawColor(0, PorterDuff.Mode.CLEAR)),那么这里为什么要进行清除操作呢,是为了避免每笔的多次重叠,例如当画笔是带有边缘模糊效果的时候,多次重叠绘制会导致模糊效果消失。这样既能实时更新画面,又减少了绘制的任务量,从而减少了总体的绘制时间。

3.控制刷新频率和局部刷新

当我们给画布所在的View中注册了onTouchEvent(),然后在其中直接调用invalidate()函数来刷新当前的视图,发现刷新频率是每秒50~60次,所以每次绘制的时间需要控制在16ms以内,采用上述方法单指绘画时每次刷新需要5ms左右,但是对于多个手指进行绘制时就会响应较慢,因为每次需要绘制的Path增多了,绘制的时间也就变长了。考虑到不同分辨率的屏幕,不同的CPU和GPU,在刷新时需要的时间也不相同,所以需要限制一下刷新的频率。例如我们在30ms才调用一次invalidate(),那么每次刷新拥有了更多的时间,这样对于多指的操作响应效果更好一些。此外,invalidate函数还有一个带参数的形式,就是invalidate(Rect r),即只刷新指定矩形框r内的数据,如果设备支持,那么该方法对减少刷新时间也是有很大帮助的。

4.异步刷新SurfaceView

SurfaceView可以在一定的时间内创建一个线程去刷新View,多个线程去刷新那么不同线程刷新结束的时间也不同,也可以展现流畅的画面,常用于游戏动画的绘制。它有一个SurfaceHolder来保存当前的view,由于默认的XML文件在解析的时候会调用View的View(Conext context,AttributeSet arrts)构造函数,所以在定义SurfaceView时要有该方法。在进行绘制时,先调用Canvas canvas = holder.lockCanvas()方法来获取并锁定Canvas,然后调用holder.unlockCanvasAndPost(canvas)将内容展示出来。

你可能感兴趣的:(Android系统下2D绘图性能提升的一些方法)