【参考链接】
群英传
canvas变换与操作http://blog.youkuaiyun.com/harvic880925/article/details/39080931
Matrix
前面说过,画布坐标系的初始状态是,坐标系的原点在画布的左上角。
但是有时候,为了绘图的方便,需要变换画布坐标系。
提供了如下函数来变换画布坐标系
当变换了画布坐标系以后,后面的draw()函数中的坐标都是对应于最新的画布坐标系。
translate(floatdx, float dy)
//平移
canvas.translate(200,200);
paintAll.setColor(Color.parseColor("#FF0000"));
canvas.drawRect(0,0,300,300,paintAll);
rotate(floatdegrees)
rotate(floatdegrees, float px, float py)
//绕(300, 300)旋转45度
canvas.rotate(45,300,300);
paintAll.setColor(Color.parseColor("#FF0000"));
canvas.drawRect(0,0,300,300,paintAll);
scale(floatsx, float sy)//如果坐标系拉伸了2倍,则变换后的300相当于是之前的600
scale(floatsx, float sy, float px, float py)
//坐标系放大两倍//新的300相当于之前的600
canvas.scale(2,2);
paintAll.setColor(Color.parseColor("#FF0000"));
canvas.drawRect(0,0,300,300,paintAll);
skew(floatsx, float sy)
//斜切//X轴倾斜60度,Y轴不变
canvas.skew(1.732f,0);
paintAll.setColor(Color.parseColor("#FF0000"));
canvas.drawRect(0,0,300,300,paintAll);
concat(Matrixmatrix)
//使用矩阵平移
Matrix matrix=newMatrix();
matrix.setValues(new float[]{1.0f,0,200,
0,1.0f,200,
0,0,1});
canvas.concat(matrix);
通过这些函数,可以方便的画出时钟刻度这样的图
protected voidonDraw(Canvas canvas) {
//获取宽高参数
mWidth= getMeasuredWidth();
mHeight= getMeasuredHeight();
int strokeWidth=5;
//画外圆
Paint paintCircle =newPaint();
paintCircle.setStyle(Paint.Style.STROKE);
paintCircle.setAntiAlias(true);
paintCircle.setStrokeWidth(strokeWidth);
canvas.drawCircle(mWidth/2,
mHeight/2,mWidth/2-strokeWidth,paintCircle);
//画刻度//每次画的都是坐标系上方中间的刻度
canvas.save();
Paint painDegree =newPaint();
paintCircle.setStrokeWidth(3);
for (inti =0;i <24;i++) {
//区分整点与非整点
if(i ==0|| i ==6|| i ==12|| i ==18) {
painDegree.setStrokeWidth(5);
painDegree.setTextSize(30);
canvas.drawLine(mWidth/2,
mHeight/2-mWidth/2+strokeWidth,
mWidth/2,
mHeight/2-mWidth/2+strokeWidth +60,
painDegree);
String degree =String.valueOf(i);
canvas.drawText(degree,
mWidth/2- painDegree.measureText(degree) /2,
mHeight/2-mWidth/2+strokeWidth +90,
painDegree);
}else{
painDegree.setStrokeWidth(3);
painDegree.setTextSize(15);
canvas.drawLine(mWidth/2,
mHeight/2-mWidth/2+strokeWidth,
mWidth/2,
mHeight/2-mWidth/2+strokeWidth+30,
painDegree);
String degree =String.valueOf(i);
canvas.drawText(degree,
mWidth/2- painDegree.measureText(degree) /2,
mHeight/2-mWidth/2+strokeWidth+60,
painDegree);
}
//通过旋转画布简化坐标运算//将画布坐标系顺时针旋转15度
canvas.rotate(15,mWidth/2,mHeight/2);
}
canvas.restore();
//画圆心
Paint paintPointer =newPaint();
paintPointer.setStrokeWidth(30);
canvas.drawPoint(mWidth/2,mHeight/2,paintPointer);
//画指针
Paint paintHour =newPaint();
paintHour.setStrokeWidth(20);
Paint paintMinute =newPaint();
paintMinute.setStrokeWidth(10);
canvas.translate(mWidth/2,mHeight/2);
canvas.drawLine(0,0,100,100,paintHour);
canvas.drawLine(0,0,100,200,paintMinute);
}
Clip
还可以使用clip()函数来圈定一块区域,设置了clip()以后,后面的draw()只有落在这个区域中才会绘制出来
可以使用Rect、Path、Region来指定这块区域
paintAll.setColor(Color.parseColor("#0000FF"));
canvas.drawRect(0,0,300,300,paintAll);
//使用矩阵平移
Matrix matrix=newMatrix();
matrix.setValues(new float[]{1.0f,0,200,
0,1.0f,200,
0,0,1});
canvas.concat(matrix);
printMatrix(matrix);
//画布坐标系原点在界面原点的(200, 200)
//在当前画布坐标系下,截取区域
canvas.clipRect(100,100,300,300);
Log.e("shadowfaxghh","clip = "+canvas.getClipBounds().toString());//这个值是相对于当前Matrix的
canvas.drawColor(Color.parseColor("#0000FF"));
//坐标系依然是上面的坐标系
paintAll.setColor(Color.parseColor("#FF0000"));
canvas.drawRect(200,200,300,300,paintAll);
canvas.translate(100,100);
Log.e("shadowfaxghh","clip = "+canvas.getClipBounds().toString());//这个值是相对于当前Matrix的
//所以并没有改变clip,但是输出变了
ImageView iv =(ImageView) findViewById(R.id.iv);
iv.setImageBitmap(bmp);
Save&Restore
在应用了Matrix和Clip以后,如何复原呢,可以借助于save()和restore()两个函数
save()记录下当前的画布坐标系的状态
saveLayer()/saveLayerAlpha()记录下当前的画布坐标系的状态,并且在当前图层上方新建一个无背景图层,后续的绘图都是在新建的图层上。可以指定图层的透明度。
restore()恢复到上一次保存时的状态。如果上次保存状态后还创建了新的图层,即saveLayer(),也会回到上一次时所在的图层。
save()的时候还可以设置记录下哪些信息,恢复的时候只会恢复记录的信息。
save()/saveLayer()会返回一个count,可以使用restoreToCount()直接恢复到该count时的状态。
save
Bitmap bmp=Bitmap.createBitmap(800,800,Bitmap.Config.ARGB_8888);//创建一个1000的画布
Canvas canvas=newCanvas(bmp);
//默认画笔
Paint paintAll=newPaint();
paintAll.setAlpha(255);//不透明度//完全不透明
paintAll.setAntiAlias(true);
paintAll.setStyle(Paint.Style.STROKE);
paintAll.setStrokeWidth(2);
paintAll.setColor(Color.parseColor("#000000"));
canvas.drawRect(0,0,300,300,paintAll);
canvas.clipRect(0,0,700,700);//clip//相当于界面原点的(0,0 ~ 700,700)
// int saveFlags = 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;
intsaveFlags = Canvas.MATRIX_SAVE_FLAG;
int save =canvas.save(saveFlags);//只保存了matrix信息
showMatrixAndClip(canvas);
//先平移
canvas.translate(300,300);
canvas.drawRect(0,0,300,300,paintAll);
canvas.clipRect(0,0,300,300);//clip发生了改变//相当于界面原点的(300,300, 600, 600)
canvas.save();
showMatrixAndClip(canvas);
//再旋转
canvas.rotate(45);
canvas.drawRect(0,0,300,300,paintAll);
//回到save时保存的状态//因为当时只保存了Matrix,所以只会还原Matrix
//不会影响clip,clip仍然是界面原点的(300, 300, 600, 600)
canvas.restoreToCount(save);
showMatrixAndClip(canvas);
canvas.drawRect(0,0,100,100,paintAll);
saveLayer
Bitmap bmp=Bitmap.createBitmap(800,800,Bitmap.Config.ARGB_8888);
Canvas canvas=newCanvas(bmp);
//默认画笔
Paint paintAll=newPaint();
paintAll.setAlpha(255);//不透明度//完全不透明
paintAll.setAntiAlias(true);
paintAll.setStyle(Paint.Style.STROKE);
paintAll.setStrokeWidth(2);
//最初的图层//第一个图层
paintAll.setColor(Color.BLUE);
canvas.drawCircle(100,100,100,paintAll);
canvas.translate(-100,-100);
//保存当前的坐标系状态(-100, -100)
//在上面创建一个新的图层//在新的图层上绘制//第二个图层
//图层所在的位置是当前坐标系的(300, 300, 500, 500)
intLAYER_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;
canvas.saveLayerAlpha(300,300,500,500,128);
canvas.drawColor(Color.BLUE);
canvas.translate(300,300);//改变坐标系
canvas.drawRect(0,0,100,100,paintAll);
canvas.restore();//恢复到上次保存时的坐标系,即回到(-100, -100),并回到第一个图层
//保存当前的坐标系状态
//再创建一个图层//第三个图层
canvas.saveLayer(400,400,600,600,paintAll,LAYER_FLAGS);
canvas.drawColor(Color.RED);
canvas.restore();
创建图层的时候需要注意
如果要在新图层上再新建一个图层,不能连续调用两次saveLayer,必须像上方代码一样saveLayer()->restore()->saveLayer()