Android 进阶之Android 绘图 三 画布Canvs以及SurfaceView
画布 Canvus
canvas.drawARGB(255,0,0,255);
canvas.drawRGB(123,34,45);
canvas.drawColor(Color.parseColor(“#343434”));
canvas.drawCircle(400,800,200,mPaint);
public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint)
canvas.drawArc(200,200,1000,1000,-90,90,false,mPaint);
canvas.drawArc(200,200,1000,1000,-90,90,true,mPaint);
canvas.drawPoint(100,100,mPaint);
canvas.drawPoints(new float[]{100,100,300,300,400,400},mPaint);
canvas.drawLine(100,100,400,400,mPaint);
canvas.drawLines(new float[]{100,100,400,600,500,100,200,200},mPaint);
//这里需要注意的是点坐标数,必须是4的倍数
canvas.drawOval(200,400,800,800,mPaint);
canvas.drawRect(100,100,600,600,mPaint);
canvas.drawRoundRect(100,100,400,400,100,60,mPaint);
mPaint.setTextSize(100);
canvas.drawText(“Hello Android”,300,400,mPaint);
Path path = new Path();
path.moveTo(200,200);
path.addRect(200,200,400,400, Path.Direction.CW);
path.addArc(200,200,800,800,100,300);
canvas.drawPath(path,mPaint);
canvaus 画布操作:
Android 使用Layer堆栈来管理画布的,画布有多个图层,整个结构如下所示,下面就针对图层操作做将要介绍:
Canvas.save() 用来保存当前的matrix和clip到私有的栈中。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。 |
Canvas && SurfaceView
在使用上面介绍的方式在View上绘图的时候,它是在GUI线程中绘制的,在GUI线程中同时也用来处理用户的交互事件,这是不大合理的,单绘制的元素太多时候,会阻塞线程,轻者影响用户体验,重则倒置ANR。
也许大家会想到将onDraw方法移到后台,但是这种方式是可行的,从后台线程修改GUI元素是不允许的。
并且我们知道View是通过系统发出VSYNC信号进行屏幕重绘的,一般刷新时间间隔为16ms,如果在16ms内可以完成所有的操作,那么将不会带来卡顿感觉,而如果超过这个时间还不能完成绘制任务,就会导致GUI线程的阻塞。
当需要快速更新View的UI,或者当前绘制阻塞GUI线程时间过长的时候,应该使用Surfaceview而不是View。SurfaceView封装的是Surface而不是Canvas。这一切原因是由于Surface使用后台线程进行绘制,而Canvase不能。
但是大家有时候会感到奇怪,为什么我们看到自定义View很多都是继承View而不是SurfaceView,这是因为SurfaceView使用的是后台线程进行绘制的,这回增加内存的消耗。所以一般能在16ms内完成绘制的话就直接继承View
所以最好在绘制高速图像,或者绘制工作不能在规定时间完成,又或者需要绘制3D图形的情况下才需要使用SurfaceView。
下面我们就来介绍下SurfaceView的用法:
在介绍SurfaceView的用法之前我们先了解下Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback。
如上面所述SurfaceView提供了一个专门用于绘制的surface。它允许在后台线程中进行绘制,你可以通过SurfaceHolder控制这个Surface的格式和尺寸。Surfaceview控制让这个Surface显示在屏幕的正确绘制位置。
Surface是Z-ordered(Z轴排序),这表明它总在自己所在窗口的后面。surfaceview在显示窗口处为Surface提供了一个可见区域,通过这个区域,才能看到Surface里面的内容。
由于这个特性我们可以放置一些覆盖图层(overlays)在Surface上面,如Button、Textview之类的。但是,需要注意的是,如果Surface上面有全透明的控件,那么随着Surface的每一次变化,这些全透明的控件就会重新渲染,这样有可能会增加系统的负担。
可以通过SurfaceHolder这个接口去访问Surface,而执行getHolder()方法可以得到SurfaceHolder接口。
SurfaceHolder是控制surface的一个抽象接口,它是一个句柄得到了这个句柄就可以得到其中的Canvas、原生缓冲器以及其它方面的内容。你可以通过SurfaceHolder来控制surface的尺寸和格式,或者修改surface的像素,监视surface的变化等等。
与直接控制SurfaceView来修改surface不同,使用SurfaceHolder来修改surface时,需要注意lockCanvas()和Callback.surfaceCreated()这两个方法。
SurfaceView有如下重要方法
abstract void addCallback(SurfaceHolder.Callback callback) 给SurfaceHolder添加一个一个回调对象。用于监视SurfaceView的某些状态,可以在这些回调方法中对某个特定事件发生的时候进行必要的处理 |
SurfaceHolder.Callback是监听surface改变的一个接口
public abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height) |
所有SurfaceView 和 SurfaceHolder.Callback的方法都应该在主线程(UI线程)里面调用。必须确保只有当Surface有效的时候,(也就是当Surface的生命周期在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()之间)才能让渲染进程访问。
下面是这几个对象的关系图
SurfaceView 绘图的模板:
public class surfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { |