Canvas&Paint[]Canvas:Matrix、Clip、Save&Restore

本文详细介绍了Canvas中的坐标系变换方法,包括平移、旋转、缩放和斜切等操作,并展示了如何利用这些变换来简化复杂的绘图任务,如绘制时钟刻度等。

【参考链接】

群英传

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()只有落在这个区域中才会绘制出来

可以使用RectPathRegion来指定这块区域


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

在应用了MatrixClip以后,如何复原呢,可以借助于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
       //
不会影响clipclip仍然是界面原点的(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()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值