Android自定义View【实战教程】5⃣️---Canvas详解及代码绘制安卓机器人

来源:互联网 发布:微商吸粉软件 编辑:程序博客网 时间:2024/05/20 07:16

友情链接:

Canvas API

Android自定义View【实战教程】3⃣️—-Paint类、Path类以及PathEffect类详解

神马是Canvas

基本概念

Canvas:可以理解为是一个为我们提供了各种工具的画布,我们可以在上面尽情的绘制(旋转,平移,缩放等等)。可以理解为系统分配给我们一个一个内存空间,然后提供了一些对这个内存空间操作的方法(API), 实际存储是在下面的bitmap。

两种画布

这里canvas可以绘制两种类型的画图,分别是view和surfaceView。
View:是普通画图,适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的。
SurfaceView:主要用在游戏,高品质动画方面的画图。
区别:在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。

Canvas坐标系与绘图坐标系

Canvas绘图中牵扯到两种坐标系:Canvas坐标系与绘图坐标系。

  • Canvas坐标系
    Canvas坐标系指的是Canvas本身的坐标系,Canvas坐标系有且只有一个,且是唯一不变的,其坐标原点在View的左上角,从坐标原点向右为x轴的正半轴,从坐标原点向下为y轴的正半轴。

  • 绘图坐标系
    Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。

我们看下面代码就可以明白:

@Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //未平移 在原点        canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴        canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴        //第一次移动        canvas.translate(200,200);        canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴        canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴        canvas.restore();        //第二次移动并旋转        canvas.translate(200,200);        canvas.rotate(30);        canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴        canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴    }

每次绘制同样的(startX, startY,stopX,stopY, paint)线,
但是我们发现平移或者旋转之后画出的线坐标发生了变化
这里写图片描述

那么有童鞋问了,如果我不想让坐标发生变化,或者再回去原点怎么搞?
别担心,只需要执行canvas.restore(),下面详细讲解。

Canvas保存和还原

  • canvas.save()
    保存当前坐标
  • canvas.restore()
    回复上一次坐标,如果有保存,回到最后一次保存的坐标,如果没保存,则会报错java.lang.IllegalStateException: Underflow in restore - more restores than saves ,要先存再取。
  • restoreToCount(int saveCount)
    回到第几次的保存坐标状态

对Canvas的操作 — 平移,旋转,缩放

Canvas平移

/**  * 画布向(dx,dy)方向平移  *   * 参数1: 向X轴方向移动dx距离  * 参数2: 向Y轴方向移动dy距离    */ canvas.translate(float dx, float dy);

Canvas缩放

/**  * 在X轴方向放大为原来sx倍,Y轴方向方大为原来的sy倍  * 默认原点为左上角 * 参数1: X轴的放大倍数  * 参数2: Y轴的放大倍数  */canvas.scale(float sx, float sy);/**  * 在X轴方向放大为原来sx倍,Y轴方向方大为原来的sy倍  * 参数1: X轴的放大倍数  * 参数2: Y轴的放大倍数  * 参数3: 原点X坐标 * 参数4: 原点Y坐标 */canvas.scale(float sx, float sy, float px, float py);

Canvas旋转

/**  * 原点为中心,旋转degrees度(顺时针方向为正方向 ) * 参数: 旋转角度  */canvas.rotate(float degrees);/**  * 以(px,py)为中心,旋转30度,顺时针方向为正方向  * 参数1: 旋转角度 * 参数2: 原点X坐标 * 参数3: 原点Y坐标  */canvas.rotate(float degrees, float px, float py);

绘制

画文字

/** * 参数1:输入的内容  * 参数2:文本x轴的位置  * 参数3:文本Y轴的位置  * 参数4:画笔对象  */drawText(String text, float x, float y,  Paint paint)/** * 参数1:输入的内容  * 参数2:要从第几个字开始绘制 * 参数3:要绘制到第几个文字  * 参数4:文本x轴的位置  * 参数5:文本Y轴的位置  * 参数6:画笔对象  */drawText(String text, int start, int end, float x, float y,Paint paint)

样例:

canvas.drawText("开始写字啦!", 200,200,mPaint);canvas.drawText("开始写字啦!",2,3, 200,400,mPaint);

这里写图片描述

画圆

/** * 参数1:圆心X  * 参数2:圆心Y  * 参数3:半径R  * 参数4:画笔对象  */  drawCircle(float cx, float cy, float radius, Paint paint)

样例:

    mPaint.setStyle(Paint.Style.STROKE);    canvas.drawCircle(300,300,80,mPaint);    mPaint.setStyle(Paint.Style.FILL);    canvas.drawCircle(300,500,80,mPaint);    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);    canvas.drawCircle(300,700,80,mPaint);

这里写图片描述

画线

/*  * 参数1:startX  * 参数2:startY  * 参数3:stopX  * 参数4:stopY  * 参数5:画笔对象  */   canvas.drawLine(float startX, float startY, float stopX, float stopY,Paint paint);/*  * 同时绘制多条线。  * 参数1:float数组:每四个一组为一条线。 * 参数2:画笔对象  */  canvas.drawLines(@Size(multiple=4)float[] pts, Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawLine(50,50,200,50,mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawLines(new float[]{200,200,300,200,300,300,300,400},mPaint);

这里写图片描述

画椭圆

/** * 参数1: 矩形 * 参数2: 画笔 * /canvas.drawOval(RectF oval, Paint paint);/** *  参数1:float left  *  参数2:float top  *  参数3:float right  *  参数4:float bottom  *  参数5:画笔 */canvas.drawOval(float left, float top, float right, float bottom, @NonNull Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawOval(new RectF(50,50,400,400),mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));mPaint.setStyle(Paint.Style.FILL_AND_STROKE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {     canvas.drawOval(50,500,700,700,mPaint);}

这里写图片描述

画弧度

/** *  参数1:RectF对象。  *  参数2:开始的角度。(水平向右为0度顺时针反向为正方向)  *  参数3:扫过的角度  *  参数4:是否和中心连线  *  参数5:画笔对象  */    canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint);/** *  参数1:float left  *  参数2:float top  *  参数3:float right  *  参数4:float bottom   *  参数5:开始的角度。(水平向右为0度顺时针反向为正方向)  *  参数6:扫过的角度  *  参数7:是否和中心连线  *  参数8:画笔对象  */canvas.drawArc(float left, float top, float right, float bottom,float startAngle, float sweepAngle, boolean useCenter,Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawArc(new RectF(50,50,400,400),45,135,true,mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {      canvas.drawArc(50,500,700,700,45,135,false,mPaint);}

这里写图片描述

矩形

/**  *  矩形  *  参数1:float left  *  参数2:float top  *  参数3:float right  *  参数4:float bottom  *  参数5:画笔 */  canvas.drawRect(float left, float top, float right, float bottom,Paint paint);  /**  *  参数1:矩形  *  参数2:画笔 */  canvas.drawRectRect r,Paint paint);  

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawRect(new RectF(50,50,400,400),mPaint);        mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawRect(50,500,700,700,mPaint);

这里写图片描述

圆角矩形

/**  *  参数1:矩形   *  参数2:x半径  *  参数3:y半径   *  参数4: 画笔  */drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)/**  *  参数1float left   *  参数2float top   *  参数3float right   *  参数4float bottom   *  参数5:x半径  *  参数6:y半径   *  参数4: 画笔  */drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawRoundRect(new RectF(50,50,400,400),20,20,mPaint);                               mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));mPaint.setStyle(Paint.Style.FILL_AND_STROKE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {    canvas.drawRoundRect(50,500,700,700,30,50,mPaint);}

这里写图片描述

画点

/**  * 参数1、2:点的x、y坐标  */  canvas.drawPoint(60, 390, p);//画一个点  /**  * 参数1:多个点,每两个值为一个点。最后个数不够两个的值,忽略。  */  canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));        canvas.drawPoint(50,50,mPaint);        mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));        canvas.drawPoints(new float[]{100,100,200,200,300, 300, 400,400,500,500,600,600},mPaint);

这里写图片描述

画图片

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);   /**  * 参数1:bitmap对象  * 参数2:图像左边坐标点  * 参数3:图像上边坐标点  */  canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint);

样例:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);canvas.drawBitmap(bitmap, 200,300, mPaint);

这里写图片描述

还有问题可以查看Canvas API

到这里基本属性就讲完了,接下来是一个练习。

代码绘制安卓小机器人

下面是代码 , 相当简单,就是计算一下坐标,就不详细讲了,有问题可以留言。

public class AndroidView extends View {    private float bodyWidth;    private float bodyHeigh;    private float armWidth;    private float armHeight;    private float legWidth;    private float legHeight;    private static final int INTERSPACE = 20;    private Paint mPaint;    private RectF bodyRect;    private RectF legRect;    private RectF armRect;    public AndroidView(Context context) {        this(context, null);    }    public AndroidView(Context context, @Nullable AttributeSet attrs) {        this(context, attrs, 0);    }    public AndroidView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init() {        mPaint = new Paint();        mPaint.setColor(getResources().getColor(android.R.color.holo_green_dark));    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        setBodyParams();        setArmParams();        setLegParams();    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.save();        //画身体        canvas.drawRoundRect(bodyRect, 20, 20, mPaint);        //画头        canvas.translate(0, -(bodyWidth / 2 + INTERSPACE));        canvas.drawArc(bodyRect, 0, -180, true, mPaint);        //画左胳膊        canvas.drawRoundRect(armRect, 30, 30, mPaint);        //画右胳膊        canvas.translate(bodyWidth + 5 * INTERSPACE, 0);        canvas.drawRoundRect(armRect, 30, 30, mPaint);        //画左腿        canvas.translate(-(bodyWidth + 7 * INTERSPACE),bodyWidth*11/10);        canvas.drawRoundRect(legRect, 30, 30, mPaint);        //画右腿        canvas.translate(2*INTERSPACE+legWidth,0);        canvas.drawRoundRect(legRect, 30, 30, mPaint);        //画左眼        canvas.translate(0,-bodyHeigh-5*INTERSPACE);        mPaint.setColor(getResources().getColor(android.R.color.white));        canvas.drawCircle(getWidth()/2,getHeight()/2,INTERSPACE/2,mPaint);        //画右眼        canvas.translate(-(2*INTERSPACE+legWidth),0);        mPaint.setColor(getResources().getColor(android.R.color.white));        canvas.drawCircle(getWidth()/2,getHeight()/2,INTERSPACE/2,mPaint);        canvas.restore();        mPaint.setTextSize(60);        mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));        canvas.drawText("我是安卓小机器人",150,100,mPaint);    }    private void setBodyParams() {        bodyWidth = getWidth() * 2 / 5;        bodyHeigh = bodyWidth;        bodyRect = new RectF();        bodyRect.left = (getWidth() - bodyWidth) / 2;        bodyRect.top = (getHeight() - bodyHeigh) / 2;        bodyRect.right = bodyRect.left + bodyWidth;        bodyRect.bottom = bodyRect.top + bodyHeigh;    }    private void setLegParams() {        legWidth = getWidth() * 1 / 13;        legHeight = getHeight() * 1 / 7;        legRect = new RectF();        legRect.left = (getWidth() - legWidth) / 2;        legRect.top = (getHeight() - legHeight) / 2;        legRect.right = legRect.left + legWidth;        legRect.bottom = legRect.top + legHeight;    }    private void setArmParams() {        armWidth = getWidth() * 1 / 13;        armHeight = getHeight() * 1 / 6;        armRect = new RectF();        armRect.left = (getWidth() - bodyWidth) / 2 - INTERSPACE * 4;        armRect.top = getHeight() / 2 + INTERSPACE * 2;        armRect.right = armRect.left + armWidth;        armRect.bottom = armRect.top + armHeight;    }}

这里写图片描述

DEMO下载地址:
http://download.csdn.net/detail/github_33304260/9841552

http://img.blog.csdn.net/20170625220855322?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2l0aHViXzMzMzA0MjYw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

8 0