Canvas、Paint和Path

边学边写写一点快餐式的知识

Canvas

中文官方文档
https://www.apiref.com/android-zh/android/graphics/Canvas.html

Canvas(Bitmap bitmap)

先来看Canvas的另一种构造方法

  • 构造一个具有指定位图的画布进行绘制。 位图必须是可变的。
    画布的初始目标密度与给定的位图密度相同。
    -这里我们就可以解决怎么将Canvas转换为Bitmap
    好说干就干
将Canvas转换为Bitmap
LinearGradient shader = new LinearGradient(0, 0, 400, 400, Color.parseColor("#BAEAF5"), Color.parseColor("#073D49"), Shader.TileMode.MIRROR);
Paint rectPaint = new Paint();
rectPaint.setShader(shader);
Bitmap bitmap = Bitmap.createBitmap(400,400, Bitmap.Config.ARGB_8888);
Canvas myCanvas = new Canvas(bitmap);
myCanvas.drawRect(0,0,400,400,rectPaint);

ok!这里我们就将myCanvas转换为Bitmap了
Bitmap bitmap = Bitmap.createBitmap(400,400, Bitmap.Config.ARGB_8888);
这句话是建一个空的bitmap

drawText

用于绘制文字

  • drawText(String text, float x, float y, Paint paint)
    使用指定的绘图在原点(x,y)处绘制文本。
 canvas.drawText("nice!", 0, this.getHeight()-100, myPaint);

drawText

用于绘制矩形(正方形也可以)

  • drawRect(float left, float top, float right, float bottom, Paint paint)
    drawRect(左边界x坐标, 上边界y坐标, 右边界x坐标, 下边界y坐标, Paint)
    -drawRect(@NonNull Rect r, @NonNull Paint paint)
    也可以用一个Rect画,下面画椭圆、圆形等等基本都有这种方法(有点麻烦就不多说了)

drawText

用于绘制圆形

  • drawCircle(float cx, float cy, float radius, Paint paint)
    以(cx,cy)为圆心绘制半径为radius的圆形
canvas.drawCircle(300, 300, 300, myPaint);

drawRoundRect

用于绘制圆角矩形

  • drawRoundRect(RectF rect, float rx, float ry, Paint paint)
  • rect:RectF对象。(不要慌Rect和RectF可以当成一个用)
  • rx:x方向上的圆角半径。
  • ry:y方向上的圆角半径。
canvas.drawRoundRect(rect, 15f, 15f, paint);

Rect和RectF区别

https://blog.csdn.net/wu56yue/article/details/50761887

drawOval

用于绘制椭圆

  • drawOval(RectF oval, @NonNull Paint paint)
  • drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)
    和上面的矩形差不多不费口舌了

translate

用于移动坐标轴

  • 先给个例子
canvas1.translate(200, 100);

也就是整个canvas1的左上顶点左移200,下移100

rotate

用于旋转坐标轴

  • rotate(float degrees, float px, float py)
    rotate(float degrees)
    两个不同的
    一个是带度数
    一个是带圆心的旋转(个人建议别用!!!!!他不是说把坐标轴转移到 px,py!!!!!!)类似下面这样


  • 还是先给个例子

canvas.rotate(90);

这个意思就是顺时针旋转90度


注意!!!!!
  1. 旋转后在移动要按照旋转后的坐标轴去移动
    详情参考>https://blog.csdn.net/iasxk/article/details/17411381
  2. 貌似先绘制的移动和旋转是不受影响的

借鉴https://blog.csdn.net/iasxk/article/details/17411381

drawPath

  • 用于按照Path路径绘制
    具体参考下面的path
    代码示例可以看下面的BMW
canvas.drawPath(path, paint);

Paint

中文官方文档
https://www.apiref.com/android-zh/android/graphics/Paint.html

setStyle

设置Paint的风格,用于控制原始图的几何形状是如何解释的(除了drawBitmap,它总是假定为Fill)。
说到这就不得不说可以填写的内容,也就是下面的枚举类了

Paint.Style

Android在用画笔的时候有三种Style,分别是
Paint.Style.STROKE 只绘制图形轮廓(描边)
Paint.Style.FILL 只绘制图形内容
Paint.Style.FILL_AND_STROKE 既绘制轮廓也绘制内容


出自
https://blog.csdn.net/csm_qz/article/details/50936606

setShader

  • 添加着色器
  • 着色器,画笔的shader定义的就是图形的着色和外观
    这么说可能不好理解,个人理解其实就是画渐变色!
  • 看似是填写一个new Shader()但其实并不是!!!!
具体填写内容
  1. LinearGradient
    --线性渲染
    霓虹灯文字,倒影图片
    LinearGradient(float x0, float y0, float x1, float y1,@ColorInt int color0, @ColorInt int color1,@NonNull TileMode tile)
    LinearGradient(起始点x坐标,起始点y坐标,终点x坐标,终点y坐标,起始颜色,最终颜色,TileMode)
    (TileMode 往下看)
Paint myPaint = new Paint();
String s1 = "#BAEAF5";
String s2 = "#073D49";
LinearGradient shader = new LinearGradient(0, this.getHeight()-100, this.getWidth(), this.getHeight(), Color.parseColor(s1), Color.parseColor(s2), Shader.TileMode.MIRROR);
myPaint.setShader(shader);
myPaint.setTextSize(400f);
canvas.drawText("nice!", 0, this.getHeight()-100, myPaint);

小拓展
看一下啊就懂
多颜色渐变

Paint myPaint = new Paint();
String s1 = "#BAEAF5";
String s2 = "#073D49";
String s3 = "#FDD9B8";
int[] colors = new int[]{ Color.parseColor(s1), Color.parseColor(s3), Color.parseColor(s2)};
float[] positions = new float[]{0f, 0.4f, 0.6f};
LinearGradient shader = new LinearGradient(0, this.getHeight() - 100, this.getWidth(), this.getHeight(),colors,positions, Shader.TileMode.MIRROR);
myPaint.setShader(shader);
myPaint.setTextSize(400f);
canvas.drawText("nice!", 0, this.getHeight() - 100, myPaint);

float[] positions = new float[]{0f, 0.4f, 0.6f};只能是0-1的数字,代表0%-40%的位置用colors第一二两个元素渐变,40%-60%的位置用colors第二三两个元素渐变,那是不是要问60%-100%用什么?用最后第三个元素补齐

  1. SweepGradient
    --渐变渲染/梯度渲染
    雷达扫描效果
    个人感觉不太常用
    SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1)
    SweepGradient(圆心x坐标, 圆心y坐标, 起始颜色, 最终颜色)
Paint myPaint = new Paint();
SweepGradient sweepGradient = new SweepGradient(300, 300, Color.parseColor("#ffffff"), Color.parseColor("#000000"));
myPaint.setShader(sweepGradient);


小拓展
多颜色渐变一样的用法位置都没变就不在这里给出了

  1. RadialGradient
    --环形渲染
    水波纹效果
    RadialGradient(float centerX, float centerY, float radius,@ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode)
    RadialGradient(圆心x坐标, 圆心y坐标,渐变的半径, 起始颜色, 最终颜色, TileMode)
Paint myPaint = new Paint();
RadialGradient radialGradient = new RadialGradient(300, 300,200, Color.parseColor("#ffffff"), Color.parseColor("#000000"), Shader.TileMode.REPEAT);
myPaint.setShader(radialGradient);
canvas.drawCircle(300, 300, 300, myPaint);

这里可以看到也用了TileMode一样的使用方法
这里故意写了一个不足圆半径的渐变


小拓展
多颜色渐变一样的用法位置都没变就不在这里给出了

  1. BitmapShader
    这个其实没啥可说的
    BitmapShader(Bitmap bitmap,TileMode tileX, TileMode tileY)
    BitmapShader(Bitmap, x轴方向TileMode, y轴方向TileMode)
    就这样
LinearGradient shader = new LinearGradient(0, 0, 400, 400, Color.parseColor("#BAEAF5"), Color.parseColor("#073D49"), Shader.TileMode.MIRROR);
Paint rectPaint = new Paint();
rectPaint.setShader(shader);
Bitmap bitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888);
Canvas myCanvas = new Canvas(bitmap);
myCanvas.drawRect(0, 0, 400, 400, rectPaint);

BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint mPaint = new Paint();
mPaint.setShader(bitmapShader);
canvas.drawRect(0, 0, 800, 800, mPaint);

这里没有用图片直接用最上面的那段代码了,前半段可以不用看

  1. ComposeShader
    --组合渲染
    这就比较恶心了
    (剽一段。。。我简化了一下)
    ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,@NonNull PorterDuff.Mode mode):

给出shaderA,shaderB和一个两者组合的模式PorterDuff,且根据此创建一个新的组合的shader。当应用了这种模式的时候,它将会拿出shaderA作为dst目标图像并且拿shaderB作为source源图像,然后这两个图像有一些折叠或者裁剪的操作后组合在一起产生一个效果,这个shader最关键的地方在PorterDuff.Mode这种模式。

首先给出了src和dst了,对应构造函数里面的shaderB和shaderA了,PorterDuff.Mode主要分为两大类。

第一部分是Alpha compositing modes即使根据透明度合成的模式

  • SRC 只展示src的shader图像;
  • SRC_OVER 就是将src覆盖在dst上面;
  • SRC_IN 只展示src覆盖dst的部分,如果是src全部覆盖在dst上面,那么就全部展示src,如果只是遮盖一部分,那么就只展示一部分;
  • SRC_ATOP 展示dst图像以及src覆盖在dst上面的那一部分;
  • DST 只展示dst的shader图像;
  • DST_OVER 现在是两个图像上下互换位置后的覆盖效果(跟SRC_OVER是反着来的);
  • DST_IN 只展示dst覆盖src的部分,如果是dst全部覆盖在src上面,那么就全部展示src,如果只是遮盖一部分,那么就只展示一部分;
  • DST_ATOP 展示src的图像以及dst覆盖在src上面的那部分;
  • CLEAR 就是清除啊,什么也不展示;
  • SRC_OUT 展示的是 src全部区域-src覆盖在dst上面的那一部分(如果src全部覆盖在dst上,那么用这个模式就展示空白了);
  • DST_OUT 展示的是 dst全部区域-dst覆盖在src上面的那一部分(如果dst全部覆盖在src上,那么用这个模式就展示空白了);
  • XOR 首先将两个图像都展示出来,然后将相交那部分裁切掉。

这就是compositing modes部分,其实大家也发现了,src打头的和dst打头的效果的不同就是将源图像和目标图像位置互换了;

第二部分是Blending modes混合模式:

不说了都差不多下面是我的一段示例(丑的一比)

LinearGradient shader1 = new LinearGradient(0, 0, 400, 400, Color.parseColor("#BAEAF5"), Color.parseColor("#073D49"), Shader.TileMode.MIRROR);
Paint rectPaint = new Paint();
rectPaint.setShader(shader1);
Bitmap bitmap1 = Bitmap.createBitmap(401, 401, Bitmap.Config.ARGB_8888);
Canvas myCanvas1 = new Canvas(bitmap1);
myCanvas1.drawRect(0, 0, 400, 400, rectPaint);

BitmapShader bitmapShader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

LinearGradient shader2 = new LinearGradient(0, 0, 400, 400, Color.parseColor("#6F4A2F"), Color.parseColor("#FABA8E"), Shader.TileMode.MIRROR);
Paint textPaint = new Paint();
textPaint.setShader(shader2);
textPaint.setTextSize(400f);
Bitmap bitmap2 = Bitmap.createBitmap(800, 800, Bitmap.Config.ARGB_8888);
Canvas myCanvas2 = new Canvas(bitmap2);
myCanvas2.drawText("燚", 200, 450, textPaint);

BitmapShader bitmapShader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Paint myPaint = new Paint();
ComposeShader composeShader = new ComposeShader(bitmapShader1, bitmapShader2, PorterDuff.Mode.SCREEN);
myPaint.setShader(composeShader);

canvas.drawRect(0, 0, 800, 800, myPaint);

这里我是canvas弄了两张bitmap(可以直接导图片的啊!)然后做了一下这个示例参考一下

TileMode
  • 拉伸形式

这个在android.graphics.Shader中的
是Shader的匿名内部类
具体就是下面这三个

  1. CLAMP --是拉伸最后一个像素铺满


  2. MIRROR ---是横向纵向不足处不断翻转镜像平铺


  3. REPEAT ---类似电脑壁纸,横向纵向不足的重复放置



    好了说完了下一方法!

参考拓展
https://blog.csdn.net/submit66/article/details/79754365
https://blog.csdn.net/huxin1875/article/details/89133025

setAntiAlias

  • true去锯齿,默认不去

setStrokeWidth

设置框线的宽度(可以这么理解)

setTextAlign

  • setTextAlign (Paint.Align align)
    设置文字对齐方式。
    这控制了文本相对于其原点的位置。
    左对齐意味着所有文本都将被绘制到其原点的右侧(即原点指定了文本的左边缘Paint.Align.LEFT)。
    具体Paint.Align添什么请往下看

Align枚举类

这个枚举类主要了解有三个

  1. CENTER
    文本在x,y原点上水平居中绘制
  2. LEFT
    文本被绘制在x,y原点的右侧
  3. RIGHT
    文本被绘制在x,y原点的左侧

Path

中文官方文档
https://www.apiref.com/android-zh/android/graphics/Path.html

各种画圆弧线直线的路径

  • 和之前的差不多就不过多bb了



    这里就展示一段(在最下面的示例BMW里面也可以找到,具体请看下面)

        Paint paint1 = new Paint();
        Path path = new Path();
        path.addCircle(200,200,150,Path.Direction.CW);
        paint1.setTextAlign(Paint.Align.CENTER);
        paint1.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path,paint1);

paint1.setTextAlign(Paint.Align.CENTER);
paint1.setStyle(Paint.Style.STROKE);这两个其实不是必要的

相关Path.Direction请往下看

Path.Direction

用于在绘制路径的时候Direction的位置填写

  1. Path.Direction.CW
    顺时针
  2. Path.Direction.CCW
    逆时针

推荐个写的不错的https://blog.csdn.net/xiangzhihong8/article/details/78278931/

其他用到的知识点

getWidth()和getMeasureWidth()

暂且先这么理解
getMeasuredWidth()是实际View的大小,与屏幕无关,而getWidth()的大小此时则是屏幕的大小。当超出屏幕后getMeasuredWidth()等于getWidth()加上屏幕之外没有显示的大小
(实际并不能这么理解)

拓展请转移https://blog.csdn.net/dmk877/article/details/49734869/

Color类

  • 常用方法parseColor
    解析颜色字符串,并返回相应的color-int。 如果字符串不能被解析,则抛出一个IllegalArgumentException异常。 支持的格式为:#RRGGBB #AARRGGBB或常用颜色名称

中文官方文档
https://www.apiref.com/android-zh/android/graphics/Color.html#parseColor(java.lang.String)
颜色选择
https://html-color-codes.info/chinese/

最后的最后附上我写的一些烂七八糟的小垃圾

  1. 哆啦A梦


public class Doraemon extends View {

    public Doraemon(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 蓝色外圆
        Paint paint0 = new Paint();
        paint0.setShader(new RadialGradient(400, 300, 300,
                Color.parseColor("#89D4FC"),
                Color.parseColor("#457A97"),
                Shader.TileMode.REPEAT));
        canvas.drawCircle(400, 300, 300, paint0);

        // 白色内圆
        paint0.setShader(new RadialGradient(400, 300, 300,
                Color.parseColor("#F8F7F7"),
                Color.parseColor("#E7E7E8"),
                Shader.TileMode.REPEAT));
        canvas.drawCircle(400, 360, 240, paint0);

        // 画眼睛
        Paint paint1 = new Paint();
        RectF ovalEye;

        paint1.setColor(Color.BLACK);
        ovalEye = new RectF(248, 18, 400, 252);
        canvas.drawOval(ovalEye, paint1);

        ovalEye = new RectF(400, 18, 552, 252);
        canvas.drawOval(ovalEye, paint1);
        
        paint1.setColor(Color.WHITE);

        paint1.setShader(new RadialGradient(400, 115, 300,
                Color.parseColor("#E7E7E8"),
                Color.WHITE,
                Shader.TileMode.REPEAT));
        ovalEye = new RectF(250, 20, 398, 250);
        canvas.drawOval(ovalEye, paint1);

        ovalEye = new RectF(402, 20, 550, 250);
        canvas.drawOval(ovalEye, paint1);

        // 画眼球
        paint1.setShader(null);
        paint1.setColor(Color.BLACK);
        canvas.drawCircle(375, 115, 20, paint1);
        canvas.drawCircle(425, 115, 20, paint1);

        // 画鼻子
        Paint paint2 = new Paint();
        paint2.setShader(new RadialGradient(390, 220, 60,
                Color.parseColor("#FBD7D3"),
                Color.parseColor("#F91E08"),
                Shader.TileMode.REPEAT));
        canvas.drawCircle(400, 225, 30, paint2);

        // 画胡子和脸
        Paint paint3 = new Paint();
        paint3.setColor(Color.BLACK);
        paint3.setStrokeWidth(8);
        canvas.drawLine(400, 255, 400, 450, paint3);

        canvas.drawLine(50, 190, 380, 290, paint3);
        canvas.drawLine(50, 320, 380, 320, paint3);
        canvas.drawLine(50, 470, 380, 350, paint3);

        canvas.drawLine(420, 290, 750, 190, paint3);
        canvas.drawLine(420, 320, 750, 320, paint3);
        canvas.drawLine(420, 350, 750, 470, paint3);

        // 画嘴
        Bitmap bitmap1 = Bitmap.createBitmap(361, 151, Bitmap.Config.ARGB_8888);
        Paint mouthPaint1 = new Paint();
        mouthPaint1.setShader(new RadialGradient(251, 180, 200,
                Color.BLACK,
                Color.RED,
                Shader.TileMode.CLAMP));
        Canvas mouthCanvas1 = new Canvas(bitmap1);
        mouthCanvas1.drawCircle(180, -30, 180, mouthPaint1);
        BitmapShader bitmapShader1 = new BitmapShader(bitmap1, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        Paint myPaint = new Paint();
        myPaint.setShader(bitmapShader1);
        canvas.translate(220, 400);
        canvas.drawRect(0, 0, 361, 151, myPaint);

        // 画舌头
        Bitmap bitmap2 = Bitmap.createBitmap(361, 151, Bitmap.Config.ARGB_8888);
        Paint mouthPaint2 = new Paint();
        mouthPaint2.setShader(new RadialGradient(361, 151, 200,
                Color.parseColor("#FBD7D3"),
                Color.parseColor("#FB7F11"),
                Shader.TileMode.CLAMP));
        Canvas mouthCanvas2 = new Canvas(bitmap2);
        RectF rectF = new RectF(150, 50, 450, 300);
        mouthCanvas2.drawOval(rectF, mouthPaint2);
        BitmapShader bitmapShader2 = new BitmapShader(bitmap2, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        ComposeShader composeShader = new ComposeShader(bitmapShader1, bitmapShader2, PorterDuff.Mode.SRC_IN);
        myPaint.setShader(composeShader);

        canvas.drawRect(100, 0, 361, 151, myPaint);
    }
}
  1. BMW


public class BMW extends View {

    private static final String BMW = "B    M    W";

    public BMW(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint0 = new Paint();
        paint0.setColor(Color.BLACK);
        canvas.drawCircle(200,200,200,paint0);

        paint0.setColor(Color.WHITE);
        paint0.setStrokeWidth(8);
        canvas.drawCircle(200,200,110,paint0);
        paint0.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(200,200,190,paint0);

        Paint paint1 = new Paint();
        Path path = new Path();
        path.moveTo(200, 50);
        path.addCircle(200,200,150,Path.Direction.CW);
        paint1.setTextAlign(Paint.Align.CENTER);

        paint1.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path,paint1);
        paint1.setStyle(Paint.Style.FILL_AND_STROKE);
        paint1.setTextSize(80);
        paint1.setColor(Color.WHITE);

        canvas.rotate(90);
        canvas.translate(0, -400);
        canvas.drawTextOnPath(BMW, path, 0, 30, paint1);
        canvas.translate(0, 400);
        canvas.rotate(-90);

        Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
        Paint paint2 = new Paint();
        paint2.setColor( Color.parseColor("#36B1FD"));
        Canvas canvas1 = new Canvas(bitmap);
        canvas1.drawRect(0, 0,100,100 ,paint2);
        canvas1.drawRect(100,100 ,200,200,paint2);
        BitmapShader bitmapShader1 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        Paint paint3 = new Paint();
        paint3.setShader(bitmapShader1);
        canvas.translate(100, 100);
        canvas.drawCircle(100, 100, 100,  paint3);

    }

}
  1. 画表盘


public class Dial extends View {
    public Dial(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /** 代表数字刻度 */
        int num = 1;

        Paint paint = new Paint();
        paint.setTextSize(50);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setStrokeWidth(10);

        //把坐标轴挪到表盘中心
        canvas.translate(500, 500);

        // 从一开始画此时y轴指向7点钟方向
        canvas.rotate(360 / 12);

        //画表盘
        for (int i = 0; i < 60; i++, canvas.rotate(360 / 60)) {
            if (i % 5 == 0) {
                canvas.drawLine(0, 450, 0, 420, paint);
                canvas.drawText(String.valueOf(num), 0, -360, paint);
                num++;
            } else {
                canvas.drawPoint(0, 435, paint);
            }
        }

        canvas.rotate(-(360 / 12 + 90));

        //调用显示时间的方法
        drawWatchHand(canvas, 1, 20, 18);

    }

    /**
     * 显示时间的方法
     *
     * @param canvas
     * @param h      小时
     * @param m      分钟
     * @param s      秒钟
     */
    private void drawWatchHand(Canvas canvas, int h, int m, int s) {
        Path pathH = new Path();
        Path pathM = new Path();
        Path pathS = new Path();

        Paint paint = new Paint();

        //秒针
        pathS.moveTo(430, -2);
        pathS.lineTo(430, 2);
        pathS.lineTo(-50, 2);
        pathS.lineTo(-60, 10);
        pathS.lineTo(-70, 0);
        pathS.lineTo(-60, -10);
        pathS.lineTo(-50, -2);
        pathS.close();

        //分针
        pathM.moveTo(0, -20);
        pathM.lineTo(-20, 0);
        pathM.lineTo(0, 20);
        pathM.lineTo(420, 0);
        pathM.close();

        //时针
        pathH.moveTo(0, -30);
        pathH.lineTo(-30, 0);
        pathH.lineTo(0, 30);
        pathH.lineTo(250, 0);
        pathH.close();

        canvas.rotate(h * (360 / 12));
        paint.setShader(new SweepGradient(0, 0, Color.parseColor("#636464"), Color.WHITE));
        canvas.drawPath(pathH, paint);
        canvas.rotate(-h * (360 / 12));

        canvas.rotate(m * (360 / 60));
        paint.setShader(new SweepGradient(0, 0, Color.parseColor("#2DA4EF"), Color.WHITE));
        canvas.drawPath(pathM, paint);
        canvas.rotate(-m * (360 / 60));

        canvas.rotate(s * (360 / 60));
        paint.setShader(new SweepGradient(0, 0, Color.RED, Color.WHITE));
        canvas.drawPath(pathS, paint);
        canvas.rotate(-s * (360 / 60));
    }
}
  1. 小猪佩奇
    原画

    成品
public class Peggy extends View {
   public Peggy(Context context, @Nullable AttributeSet attrs) {
       super(context, attrs);
   }

   @Override
   protected void onDraw(Canvas canvas) {
       super.onDraw(canvas);
       Paint paint = new Paint();
       paint.setStrokeWidth(3);
       paint.setColor(Color.BLACK);
       paint.setStyle(Paint.Style.STROKE);

       //画头轮廓
       Path path = new Path();
       path.moveTo(166,111);
       path.quadTo(210,112,220,85);
       canvas.drawPath(path,paint);

       //画鼻子
       canvas.translate(220,85);
       canvas.rotate(-20);
       canvas.drawOval(-15,0,15,-55,paint);
       paint.setStrokeWidth(8);
       canvas.drawPoint(7,-27,paint);
       canvas.drawPoint(-7,-27,paint);
       paint.setStrokeWidth(3);
       canvas.translate(0,-55);
       canvas.rotate(20);

       Path path1 = new Path();
       path1.quadTo(156-196,24-27,127-196,39-27);

       //画右耳
       path1.moveTo(125-196,46-27);
       path1.quadTo(127-196,4-27,110-196,0-27);
       path1.quadTo(80-196,12-27,111-196,50-27);

       //继续画头轮廓
       path1.moveTo(112-196,47-27);
       path1.lineTo(92-196,56-27);

       //画左耳
       path1.moveTo(95-196,63-27);
       path1.cubicTo(80-196,8-27,48-196,16-27,78-196,73-27);

       //继续画头轮廓
       path1.moveTo(74-196,70-27);
       path1.quadTo(20-196,180-27,112-196,206-27);
       path1.quadTo(209-196,201-27,176-196,95-27);

       //画嘴
       path1.moveTo(113-196,145-27);
       path1.cubicTo(140-196,176-27,169-196,171-27,168-196,130-27);

       //画瞳孔
       paint.setStrokeWidth(10);
       canvas.drawPoint(119-196,70-27,paint);
       canvas.drawPoint(150-196,55-27,paint);

       //画眼睛
       paint.setStrokeWidth(3);
       canvas.drawOval(106-196,55-27,132-196,87-27,paint);
       canvas.drawCircle(150-196,55-27,10,paint);

       //画身体
       path1.moveTo(70-196,183-27);
       path1.quadTo(40-196,209-27,39-196,300-27);
       path1.lineTo(204-196,300-27);
       path1.quadTo(203-196,230-27,171-196,186-27);

       //画手
       path1.moveTo(58-196,207-27);
       path1.lineTo(8-196,233-27);
       path1.moveTo(174-196,200-27);
       path1.lineTo(233-196,233-27);
       path1.moveTo(222-196,240-27);
       path1.quadTo(200-196,213-27,240-196,215-27);
       path1.moveTo(3-196,217-27);
       path1.quadTo(30-196,217-27,17-196,240-27);

       //画脚
       path1.moveTo(84-196,300-27);
       path1.lineTo(84-196,332-27);
       path1.moveTo(152-196,300-27);
       path1.lineTo(152-196,332-27);

       canvas.drawPath(path1,paint);

       //还原坐标轴
       canvas.rotate(-20);
       canvas.translate(0,55);
       canvas.rotate(20);
       canvas.translate(-220,-85);

       //画脚
       Path path2 = new Path();
       path2.quadTo(-4,5,0,10);
       path2.lineTo(30,12);
       path2.quadTo(36,5,30,-2);
       path2.close();
       paint.setStyle(Paint.Style.FILL_AND_STROKE);
       canvas.translate(80,340);
       canvas.drawPath(path2,paint);
       canvas.translate(-75,-330);
       canvas.translate(144,330);
       canvas.drawPath(path2,paint);
       canvas.translate(-144,-330);
       paint.setStyle(Paint.Style.STROKE);

       //画尾巴

       paint.setStrokeWidth(2);
       Path path3 = null;
       canvas.translate(40,262);
       int start = 90;
       for (int i = 0; i < 450; i++) {
           path3 = new Path();
           RectF r = new RectF(-12, -12, 12, 12);
           path3.arcTo(r,start,1);
           start++;
           canvas.drawPath(path3,paint);
           canvas.translate((float) -0.06,0);
       }
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容