自定义一个TransformView继承自view,在构造方法中初始化画笔(paint):
public class TransformView extends View {
private Paint mPaint;
public TransformView(Context context) {
this(context, null);
}
public TransformView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TransformView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(4);
mPaint.setStyle(Paint.Style.STROKE);
}
...
}
画笔初始化完成后在ondraw方法中,我们今天的主角canvas登场。
canvas.translate(50, 50);//将画布坐标右移50,下移50
canvas.drawRect(0,0, 400, 400, mPaint);//画一个红色矩形
canvas.translate(50, 50);//将画布坐标再次右移50,下移50
mPaint.setColor(Color.GRAY);//画笔颜色改为灰色
canvas.drawRect(0,0, 400, 400, mPaint);//画一个相对画布不变的灰色矩形
canvas.drawLine(0, 0, 600,600, mPaint);//画一条线,起点左边0,0,终点坐标600,600
从代码和效果图可以看出,canvas平移后,起点坐标也就跟着移动了,需要注意的是translate(float dx, float dy)函数的两个参数dx,dy是相对量,是相对平移前画布的坐标,比如连续执行两次canvas.translate(50, 50),其实画布是相对之前,像右和下各平移了100px。
mPaint.setColor(Color.RED);//画笔颜色改为红色
canvas.drawRect(200,200, 700,700, mPaint);//画一个红色矩形
canvas.scale(0.5f, 0.5f);//画布缩放为原来的0.5倍
mPaint.setColor(Color.GRAY);//画笔颜色改为灰色
canvas.drawRect(200,200, 700,700, mPaint);//画一个相对画布不变的灰色矩形
canvas.drawLine(200,200, 600, 600, mPaint);//画一条线,起点左边200,200,终点坐标600,600
canvas.scale(0.5f, 0.5f);是将画布的横坐标和纵坐标都压缩为原来的一半,但是压缩后的坐标原点和压缩前的坐标原点在同一个地方,都是画布的左上角,那么肯定有指定左边原点的方法,那就是4个参数的scale,scale(float sx, float sy, float px, float py),其中sx和sy还是缩放系数,px和py则是相对于缩放前画布的坐标,该点为缩放前后画布的重合点,看起源码也很容易理解
public final void scale(float sx, float sy, float px, float py) {
if (sx == 1.0f && sy == 1.0f) return;
translate(px, py);
scale(sx, sy);
translate(-px, -py);
}
其实scale(float sx, float sy, float px, float py)相当于3步操作,我们对上面的栗子进行改造
mPaint.setColor(Color.RED);//画笔颜色改为红色
canvas.drawRect(200,200, 700,700, mPaint);//画一个红色矩形
canvas.scale(0.5f, 0.5f, 700,700);//画布缩放为原来的0.5倍,并且缩放前后坐标重叠点在缩放前的700,700处
mPaint.setColor(Color.GRAY);//画笔颜色改为灰色
canvas.drawRect(200,200, 700,700, mPaint);//画一个相对画布不变的灰色矩形
canvas.drawLine(200,200, 600, 600, mPaint);//画一条线,起点左边200,200,终点坐标600,600
canvas.translate(50,50);//先将画布右下平移50px,方便查看效果
mPaint.setColor(Color.RED);//画笔颜色改为红色
canvas.drawRect(0,0, 700,700, mPaint);//画一个红色矩形
canvas.rotate(45);//旋转45°
mPaint.setColor(Color.GRAY);//更改画笔为灰色
canvas.drawRect(0,0, 700,700, mPaint);//画一个大小相同灰色矩形
可以看到rotate(float degrees)的角度degrees是顺时针旋转的,一个参数的旋转中心是在画布的0,0坐标处,当然也可以指定旋转中心,rotate(float degrees, float px, float py)即指定旋转中心为(x,y),改造上面的代码
canvas.translate(50,50);//先将画布右下平移50px,方便查看效果
mPaint.setColor(Color.RED);//画笔颜色改为红色
canvas.drawRect(0,0, 700,700, mPaint);//画一个红色矩形
canvas.rotate(45, 350, 350); //px, py表示旋转中心的坐标
mPaint.setColor(Color.GRAY);//更改画笔为灰色
canvas.drawRect(0,0, 700,700, mPaint);//画一个大小相同灰色矩形
倾斜操作skew(float sx, float sy)有2个参数,第一个参数sx代表在X方向倾斜,Y轴逆时针旋转,第二个参数sy代表在y方向倾斜, X轴顺时针旋转,该函数用的较少,笔者也似懂非懂
canvas.drawRect(0,0, 400, 400, mPaint);
canvas.skew(1, 0); //在X方向倾斜45度,Y轴逆时针旋转45
mPaint.setColor(Color.GRAY);
canvas.drawRect(0, 0, 400, 400, mPaint);
简单讲就是裁剪画布,画布裁剪后只在裁剪区域内绘制有效,同理还有反向切割,反向切割后只在反向切割范围内有效
canvas.drawRect(200, 200,700, 700, mPaint);
mPaint.setColor(Color.GRAY);
canvas.drawRect(200, 800,700, 1300, mPaint);
canvas.clipRect(200, 200,700, 700); //画布被裁剪
canvas.drawCircle(100,100, 100,mPaint); //坐标超出裁剪区域,无法绘制
canvas.drawCircle(300, 300, 100, mPaint); //坐标区域在裁剪范围内,绘制成功
canvas.drawRect(200, 200,700, 700, mPaint);
mPaint.setColor(Color.GRAY);
canvas.drawRect(200, 800,700, 1300, mPaint);
canvas.clipOutRect(200,200,700,700); //画布裁剪外的区域
canvas.drawCircle(100,100,100,mPaint); //坐标区域在裁剪范围内,绘制成功
canvas.drawCircle(300, 300, 100, mPaint);//坐标超出裁剪区域,无法绘制
我们可以通过矩阵实现平移、缩放、旋转等操作
//矩阵
canvas.drawRect(0,0,700,700, mPaint);
Matrix matrix = new Matrix();
matrix.setTranslate(50,50);
// matrix.setRotate(45);
// matrix.setScale(0.5f, 0.5f);
canvas.setMatrix(matrix);
mPaint.setColor(Color.GRAY);
canvas.drawRect(0,0,700,700, mPaint);
1.canvas内部对于状态的保存存放在栈中;
2.可以多次调用save保存canvas的状态,并且可以通过getSaveCount方法获取保存的状态个数;
3.可以通过restore方法返回最近一次save前的状态,也可以通过restoreToCount返回指定save状态。指定save状态之后的状态全部被清除;
4.saveLayer可以创建新的图层,之后的绘制都会在这个图层之上绘制,直到调用restore方法。
注意:绘制的坐标系不能超过图层的范围, saveLayerAlpha对图层增加了透明度信息。
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印1
canvas.drawRect(200, 200, 700, 700, mPaint);
int state = canvas.save();//save一次,state=1,getSaveCount+1 = 2
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印2
canvas.translate(50, 50);
mPaint.setColor(Color.GRAY);
canvas.drawRect(0, 0, 500, 500, mPaint);
canvas.save();//save一次,getSaveCount+1 = 3
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印3
canvas.translate(50, 50);
mPaint.setColor(Color.BLUE);
canvas.drawRect(0, 0, 500, 500, mPaint);
canvas.restore();//restore一次,getSaveCount -1 =2
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印2
canvas.restore();//restore一次,getSaveCount -1 = 1
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印1
canvas.restoreToCount(state);//恢复到statue = 1之前,即getSaveCount = 1
Log.e(TAG, "onDraw: " + canvas.getSaveCount());//打印1
2019-09-05 22:25:19.356 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 1
2019-09-05 22:25:19.356 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 2
2019-09-05 22:25:19.356 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 3
2019-09-05 22:25:19.357 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 2
2019-09-05 22:25:19.357 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 1
2019-09-05 22:25:19.358 18398-18398/com.itzb.paintdemo E/SaveRestoreView: onDraw: 1
另外离屏绘制在图层混合模式(Xfermode)已经使用过,这里在摘出来
canvas.drawRect(200,200, 700,700, mPaint);
int layerId = canvas.saveLayer(0,0, 700, 700, mPaint);//离屏绘制开始
mPaint.setColor(Color.GRAY);
Matrix matrix = new Matrix();
matrix.setTranslate(100,100);
canvas.setMatrix(matrix);
canvas.drawRect(0,0,700,700, mPaint); //由于平移操作,导致绘制的矩形超出了图层的大小,所以绘制不完全
canvas.restoreToCount(layerId);//离屏绘制结束
mPaint.setColor(Color.RED);
canvas.drawRect(0,0,100,100, mPaint);