Canvas 在一般的情况下可以看作是一张画布,所有的绘图操作如drawBitmap, drawCircle都发生在这张画布上,这张画板还定义了一些属性比如Matrix,颜色等等。
但是如果需要实现一些相对复杂的绘图操作,比如多层动画,地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)。
Canvas提供了图层(Layer)支持,缺省情况可以看作是只有一个图层Layer。如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层,对于这些Layer是按照“栈结构“来管理的:
创建一个新的Layer到“栈”中,可以使用saveLayer, savaLayerAlpha, 从“栈”中推出一个Layer,可以使用restore,restoreToCount。但Layer入栈时,后续的DrawXXX操作都发生在这个Layer上,而Layer退栈时,就会把本层绘制的图像“绘制”到上层或是Canvas上,在复制Layer到Canvas上时,可以指定Layer的透明度(Layer),这是在创建Layer时指定的:
public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags)
本例Layers 介绍了图层的基本用法:Canvas可以看做是由两个图层(Layer)构成的,为了更好的说明问题,我们将代码稍微修改一下,缺省图层绘制一个红色的圆,在新的图层画一个蓝色的圆,新图层的透明度为0×88。
1 |
@Override protected void onDraw(Canvas canvas) {
|
2 |
canvas.drawColor(Color.WHITE); |
4 |
canvas.translate( 10 , 10 ); |
6 |
mPaint.setColor(Color.RED); |
7 |
canvas.drawCircle( 75 , 75 , 75 , mPaint); |
9 |
canvas.saveLayerAlpha( 0 , 0 , 200 , 200 , 0x88 , LAYER_FLAGS); |
12 |
mPaint.setColor(Color.BLUE); |
13 |
canvas.drawCircle( 125 , 125 , 75 , mPaint); |
在调用canvas.saveLayerAlpha 创建一个新图层之后,后续的canvas.drawCircle 都会发生的这个新图层上, canvas.restore() 将这个新图层绘制的图像“复制”到Canvas的缺省图层上,透明度为0×88。
public class Layers extends GraphicsActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private static final int LAYER_FLAGS = Canvas.MATRIX_SAVE_FLAG |Canvas.CLIP_SAVE_FLAG |Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |Canvas.FULL_COLOR_LAYER_SAVE_FLAG |Canvas.CLIP_TO_LAYER_SAVE_FLAG;//ALL_SAVE_FLAG; //该参数当画布执行restore 操作时,需要restore的状态属性分别对应:matrix,clip,alpha,color,越界的clip,everythingprivate Paint mPaint;public SampleView(Context context) {super(context);setFocusable(true);mPaint = new Paint();mPaint.setAntiAlias(true);}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE); //第一层canvas.translate(10, 10);mPaint.setColor(Color.RED);canvas.drawCircle(75, 75, 75, mPaint);canvas.saveLayerAlpha(0, 0, 200, 200, 0x88, LAYER_FLAGS);// 第二层的 透明度是0x88mPaint.setColor(Color.BLUE);canvas.drawCircle(125, 125, 75, mPaint);canvas.restore(); // canvas.restore() 将这个新图层绘制的图像“复制”到Canvas的缺省图层上,透明度为0×88。}}
}
public class MeasureText extends GraphicsActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private Paint mPaint;private float mOriginX = 10;private float mOriginY = 80;public SampleView(Context context) {super(context);setFocusable(true);mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setStrokeWidth(5);mPaint.setStrokeCap(Paint.Cap.ROUND);mPaint.setTextSize(64);mPaint.setTypeface(Typeface.create(Typeface.SERIF,Typeface.ITALIC));}private void showText(Canvas canvas, String text, Paint.Align align) {// mPaint.setTextAlign(align);Rect bounds = new Rect();float[] widths = new float[text.length()];int count = mPaint.getTextWidths(text, 0, text.length(), widths);float w = mPaint.measureText(text, 0, text.length());//使用paint 测量text的长度mPaint.getTextBounds(text, 0, text.length(), bounds); //拿到字体的文字的矩形区域mPaint.setColor(0xFF88FF88);canvas.drawRect(bounds, mPaint);//绘制背景mPaint.setColor(Color.BLACK);canvas.drawText(text, 0, 0, mPaint);//绘制文字float[] pts = new float[2 + count*2]; float x = 0;float y = 0;pts[0] = x;pts[1] = y;for (int i = 0; i < count; i++) {x += widths[i];pts[2 + i*2] = x;pts[2 + i*2 + 1] = y;}mPaint.setColor(Color.RED);mPaint.setStrokeWidth(0);canvas.drawLine(0, 0, w, 0, mPaint);//绘制下线mPaint.setStrokeWidth(5);canvas.drawPoints(pts, 0, (count + 1) << 1, mPaint);//绘制下面的点号 pts就是位置 :x0 y0 x1y1 x2 y2 ...}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);canvas.translate(mOriginX, mOriginY);showText(canvas, "Measure", Paint.Align.LEFT);canvas.translate(0, 80);showText(canvas, "wiggy!", Paint.Align.CENTER);canvas.translate(0, 80);showText(canvas, "Text", Paint.Align.RIGHT);}}
}
public class PathEffects extends GraphicsActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private Paint mPaint;private Path mPath;private PathEffect[] mEffects;private int[] mColors;private float mPhase;private static void makeEffects(PathEffect[] e, float phase) {e[0] = null; // no effecte[1] = new CornerPathEffect(10); //柔化圆角CornerPathEffect 修改路径上线段连接处的显示模式(从尖角改为可以指定圆弧半径的圆角连接)。//DashPathEffect 虚线线显示效果,使用数组来指定需线和间隔的长度。数组中序数(下标)为偶数表示虚线的实线长度,//奇数为虚线的间隔长度。 只对Paint设为STROKE 或STROKE_AND_FILL时有效。e[2] = new DashPathEffect(new float[] {10, 5, 5, 5}, phase); e[3] = new PathDashPathEffect(makePathDash(), 12, phase,PathDashPathEffect.Style.ROTATE);e[4] = new ComposePathEffect(e[2], e[1]);e[5] = new ComposePathEffect(e[3], e[1]);
// e[0] 不含任何PathEffect,缺省绘制Path的风格,单色实线,连接处为尖角。
// e[1] 连接使用圆弧连接。
// e[2] 使用缺省的虚线绘制路径。
// e[3] 使用自定义的图形(本例为一箭头)的虚线绘制路径
// e[4] 为使用e[2]和e[1]综合效果,虚线并圆弧连接。
// e[5] 为使用e[3]和e[1]综合效果,自定义虚线并圆弧连接。// CornerPathEffect 修改路径上线段连接处的显示模式(从尖角改为可以指定圆弧半径的圆角连接)。
// DashPathEffect 虚线显示效果,使用数组来指定需线和间隔的长度。数组中序数为偶数表示虚线的实线长度,奇数为虚线的间隔长度。 只对Paint设为STROKE 或STROKE_AND_FILL时有效。
// PathDashPathEffect 和DashPathEffect类似显示虚线,但可以自定义虚线的模式(DashPathEffect使用的是矩形,而PathDashPathEffect允许使用Path对象自定义虚线的模式),同样只对Paint设为STROKE 或STROKE_AND_FILL时有效。
// ComposePathEffect 允许组合两个PathEffect (outer ,inner) , 分两次综合结果ComposePathEffect = Outer(inner(path)).
// SumPathEffect 组合两个两个PathEffect (first, second) SumPathEffect=first(path) + second(path).
// DiscretePathEffect 将路径划分成指定长度的线段,然后把每条线段随机偏移原来的位置。}public SampleView(Context context) {super(context);setFocusable(true);setFocusableInTouchMode(true);mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(6);mPath = makeFollowPath();mEffects = new PathEffect[6];mColors = new int[] { Color.BLACK, Color.RED, Color.BLUE,Color.GREEN, Color.MAGENTA, Color.BLACK};}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);RectF bounds = new RectF();mPath.computeBounds(bounds, false);canvas.translate(10 - bounds.left, 10 - bounds.top);makeEffects(mEffects, mPhase);mPhase += 1;invalidate();for (int i = 0; i < mEffects.length; i++) {mPaint.setPathEffect(mEffects[i]);mPaint.setColor(mColors[i]);canvas.drawPath(mPath, mPaint);canvas.translate(0, 28);}}@Override public boolean onKeyDown(int keyCode, KeyEvent event) {switch (keyCode) {case KeyEvent.KEYCODE_DPAD_CENTER:mPath = makeFollowPath(); //按方向键上“中间键”将随机产生一个新的绘制路径。return true;}return super.onKeyDown(keyCode, event);}private static Path makeFollowPath() { //绘制 路径 android.graphics.Path允许使用线段,二次曲线,三次曲线构成路径(类似SVG)Path p = new Path();p.moveTo(0, 0);for (int i = 1; i <= 15; i++) {p.lineTo(i*20, (float)Math.random() * 35);}return p;}private static Path makePathDash() {// makePathDash定义了自定义虚线类型上的小箭头图案:Path p = new Path();p.moveTo(4, 0);p.lineTo(0, -4);p.lineTo(8, -4);p.lineTo(12, 0);p.lineTo(8, 4);p.lineTo(0, 4);return p;}}
}