边学边写写一点快餐式的知识
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);
-
关于文字位置
https://www.jianshu.com/p/e96d28f68cd6
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度
注意!!!!!
- 旋转后在移动要按照旋转后的坐标轴去移动
详情参考>https://blog.csdn.net/iasxk/article/details/17411381 - 貌似先绘制的移动和旋转是不受影响的
借鉴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()但其实并不是!!!!
具体填写内容
- 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%用什么?用最后第三个元素补齐
- 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);
小拓展
多颜色渐变一样的用法位置都没变就不在这里给出了
- 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一样的使用方法
这里故意写了一个不足圆半径的渐变
小拓展
多颜色渐变一样的用法位置都没变就不在这里给出了
- 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);
这里没有用图片直接用最上面的那段代码了,前半段可以不用看
- 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的匿名内部类
具体就是下面这三个
-
CLAMP --是拉伸最后一个像素铺满
-
MIRROR ---是横向纵向不足处不断翻转镜像平铺
-
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枚举类
这个枚举类主要了解有三个
- CENTER
文本在x,y原点上水平居中绘制 - LEFT
文本被绘制在x,y原点的右侧 - 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的位置填写
- Path.Direction.CW
顺时针 - 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/
最后的最后附上我写的一些烂七八糟的小垃圾
-
哆啦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);
}
}
-
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);
}
}
-
画表盘
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));
}
}
- 小猪佩奇
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);
}
}
}