Android从0到1绘制冬奥吉祥物冰墩墩,实现一户一墩
共 9259字,需浏览 19分钟
·
2022-03-01 18:41
北京冬奥虽然已经结束了,但吉祥物冰墩墩的热度依然不减,毫不疑问冰墩墩是今年冬奥名副其实的新晋顶流。现在网上还流传着一句很贴切的话,那就是一“墩”难求。既然身无余“墩”,那就画一个梦中情“墩”,学会绘制冰墩墩,手动实现一户一墩不是梦,赶快学起来吧。
先来看看最终效果图:
根据效果图,我们大概可以看出这得自定义绘制,此冰墩墩基本是由点、矩形、椭圆、圆等常规图形构成,重点还是使用了比较多的曲线绘制而成,常规图形可以使用Canvas绘制,曲线则要使用Path绘制贝塞尔曲线,我们先来回顾一下涉及的知识点。
一、涉及知识点
回顾一下画笔paint涉及的API:
回顾一下canvas绘制涉及的API:
回顾一下path涉及的API:
具体到每个API这里就不展开讲解了,如有需要可以查阅相关资料。
二、实现步骤
1、首先画一个椭圆,如下图所示:
这个还是比较简单的,绘制长半轴在y轴的椭圆,椭圆具体数值短半轴为屏幕宽度/4,长半轴为屏幕高度/6,具体代码如下:
private void init() {
//初始化矩阵
rectF = new RectF();
//创建画笔
paint = new Paint();
//设置画笔颜色
paint.setColor(Color.BLACK);
//设置画笔宽度
paint.setStrokeWidth(10);
//设置画笔填充方法:描边
paint.setStyle(Paint.Style.STROKE);
//设置抗锯齿
paint.setAntiAlias(true);
paintBlack.setColor(Color.BLACK);
paintBlack.setStrokeWidth(10);
paintBlack.setStyle(Paint.Style.FILL);
paintBlack.setAntiAlias(true);
paintRed.setColor(Color.parseColor("#AA292D"));
paintRed.setStrokeWidth(10);
paintRed.setStyle(Paint.Style.FILL);
paintRed.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//设置中心点
mCentenWith = w / 2;
mCentenHight = h / 2;
//设置椭圆长、短半轴
mWith = mCentenWith / 2;
mHight = mCentenHight / 3;
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制头部
//将坐标原点移动到屏幕中心
canvas.translate(mCentenWith, mCentenHight);
//设置矩阵坐标
rectF.set(-mWith, -mHight, mWith, mHight);
//画椭圆
canvas.drawOval(rectF, paint);
}
2、然后再画两个耳朵,并填满颜色,如图所示:
这里绘制用到的是二阶贝塞尔曲线,还涉及到椭圆上的点x、y坐标的确认,根据参数方程:
x=acost;y=bsint;焦点在y轴上,短半轴为a,长半轴为b,t为坐标角度
这里二阶贝塞尔曲线起点设置在椭圆角度为π/5处,而终点设置在椭圆角度为π/3处,具体代码如下所示:
private Path mPathEar1 = new Path();
private Path mPathEar2 = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右耳朵
// x=acost;y=bsint;焦点在y轴上,短半轴为a,长半轴为b
//起点
float ear_x1 = (float) (mWith * Math.cos(Math.PI / 5));
float ear_y1 = (float) (mHight * Math.sin(Math.PI / 5));
//终点
float ear_x2 = (float) (mWith * Math.cos(Math.PI / 3));
float ear_y2 = (float) (mHight * Math.sin(Math.PI / 3));
//移动起点位置
mPathEar1.moveTo(ear_x1, -ear_y1);
//绘制曲线
mPathEar1.quadTo(mWith, -mHight, ear_x2, -ear_y2);
canvas.drawPath(mPathEar1, paintBlack);
//绘制左耳朵
//移动起点位置
mPathEar2.moveTo(-ear_x1, -ear_y1);
//绘制曲线
mPathEar2.quadTo(-mWith, -mHight, -ear_x2, -ear_y2);
canvas.drawPath(mPathEar2, paintBlack);
}
3、接下来就是绘制冰墩墩的两只手臂啦,在右手臂再画一个小心心在手上颜色填满,如图所示:
这里就要用到三阶贝塞尔曲线啦,这里的难点主要是控制点的坐标不好掌握,我这里绘制的点都是通过测试获取的,所以调试时间还是用的比较多的,最好的请设计帮忙,通过ps给个大致估算,这里只是跟着感觉试出来的,这里的爱心是通过四段三阶贝塞尔曲线合成的爱心,具体代码如下:
private Path mPathHandRight = new Path();
private Path mPathHandLeft = new Path();
private Path mPathHeart = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右手
//终点
float hand_x = (float) (mWith * Math.cos(Math.PI / 8));
float hand_y = (float) (mHight * Math.sin(Math.PI / 8));
//移动起点
mPathHandRight.moveTo(mWith, 0);
//绘制曲线
mPathHandRight.cubicTo(mWith * 7 / 6, -mHight / 2, 2 * mWith, -mHight / 4, hand_x, hand_y);
canvas.drawPath(mPathHandRight, paintBlack);
//绘制右手红心,这里的爱心是通过四段三阶贝塞尔曲线合成的爱心
//重新设置坐标原点,将画布平移到右手位置
canvas.translate(mWith * 9 / 7, -mHight / 8);
//将画布旋转30度
canvas.rotate(30);
//移动起点
mPathHeart.moveTo(0, -2);
//绘制曲线
mPathHeart.cubicTo(mWith / 16, -mHight / 16, mWith / 12, -mHight / 16, mWith / 12, 0);
canvas.drawPath(mPathHeart, paintRed);
//绘制曲线
mPathHeart.cubicTo(mWith / 12, 0, mWith / 12, mHight / 16, 0, mHight / 12);
canvas.drawPath(mPathHeart, paintRed);
//移动起点
mPathHeart.moveTo(0, -2);
//绘制曲线
mPathHeart.cubicTo(-mWith / 16, -mHight / 16, -mWith / 12, -mHight / 16, -mWith / 12, 0);
canvas.drawPath(mPathHeart, paintRed);
//绘制曲线
mPathHeart.cubicTo(-mWith / 12, 0, -mWith / 12, mHight / 16, 0, mHight / 12);
canvas.drawPath(mPathHeart, paintRed);
//将画布还原回去
canvas.rotate(-30);
canvas.translate(-mWith * 9 / 7, mHight / 8);
//绘制左手
//移动起点
mPathHandLeft.moveTo(-mWith, 0);
//绘制曲线
mPathHandLeft.cubicTo(-mWith * 5 / 3, mHight / 2, -mWith * 7 / 6, mHight, -hand_x, hand_y);
canvas.drawPath(mPathHandLeft, paintBlack);
}
4、接下来就是画冰墩墩的两只脚啦,颜色填满,如图所示:
这里也要用到三阶贝塞尔曲线,控制点也是通过椭圆的长短半轴试出来的,具体代码如下所示:
private Path mPathArmRight = new Path();
private Path mPathArmLeft = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右脚
//起点
float arm_x = (float) (mWith * Math.cos(Math.PI / 3));
float arm_y = (float) (mHight * Math.sin(Math.PI / 3));
//终点
float arm_final_x = (float) (mWith * Math.cos(Math.PI * 4 / 9));
float arm_final_y = (float) (mHight * Math.sin(Math.PI * 4 / 9));
//移动起点
mPathArmRight.moveTo(arm_x, arm_y);
//绘制曲线
mPathArmRight.cubicTo(arm_x + mWith / 12, mHight * 3 / 2, arm_x / 4, mHight * 3 / 2, arm_final_x, arm_final_y);
canvas.drawPath(mPathArmRight, paintBlack);
//绘制左脚
//移动起点
mPathArmLeft.moveTo(-arm_x, arm_y);
//绘制曲线
mPathArmLeft.cubicTo(-(arm_x + mWith / 12), mHight * 3 / 2, -(arm_x / 4), mHight * 3 / 2, -arm_final_x, arm_final_y);
canvas.drawPath(mPathArmLeft, paintBlack);
}
5、接下来就是绘制头部轮廓啦,先在内部绘制类似馒头的五环形状,如图所示:
这里主要用到的也是三阶贝塞尔曲线,控制点数值也是模拟出来的,具体如下所示:
private Path mPathBody = new Path();
private int[] colors = {Color.parseColor("#88C46A"), Color.parseColor("#E5C267"),
Color.parseColor("#6872AE"), Color.parseColor("#AE2C6D"), Color.parseColor("#6CC4F3")};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制脸部轮廓
for (int i = 0; i < 5; i++) {
float r = mWith * 7 / 8 - 10 * i;
mPathBody.reset();
//移动起点
mPathBody.moveTo(-r, 0);
//控制点
float x1 = mWith * 3 / 4 - 10 * i;
float y1 = mHight / 2 - 10 * i;
mPathBody.cubicTo(-x1, y1, x1, y1, r, 2);
//控制点
float x2 = mWith - 10 * i;
float y2 = mHight * 5 / 4 - 10 * i;
mPathBody.cubicTo(x2, -y2, -x2, -y2, -r, 2);
//设置颜色
paint.setColor(colors[i]);
canvas.drawPath(mPathBody, paint);
}
//还原设置
paint.setColor(Color.BLACK);
}
6、接下来就是画眼睛啦,画两个椭圆黑眼圈,如图所示:
这里还是比较简单的,就是绘制椭圆,然后通过旋转,具体代码如下所示:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右眼睛轮廓
//绘制矩阵
rectF.set(mWith / 4, -mHight * 2 / 5, mWith * 3 / 4, mHight / 6);
//旋转角度
canvas.rotate(-40);
//绘制椭圆
canvas.drawOval(rectF, paintBlack);
//还原角度
canvas.rotate(40);
//绘制左眼睛轮廓
//绘制矩阵
rectF.set(-mWith / 4, -mHight * 2 / 5, -mWith * 3 / 4, mHight / 6);
//旋转角度
canvas.rotate(40);
//绘制椭圆
canvas.drawOval(rectF, paintBlack);
//还原角度
canvas.rotate(-40);
}
椭圆里再画两个大眼睛,如图所示:
这里绘制大眼睛还是比较简单的,就是绘制两个圆,代码如下所示:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右眼睛
paint.setColor(Color.WHITE);
canvas.drawCircle(mWith / 4, -mHight * 2 / 5, mWith / 6, paint);
//还原设置
paint.setColor(Color.BLACK);
//绘制左眼睛
paint.setColor(Color.WHITE);
canvas.drawCircle(-mWith / 4, -mHight * 2 / 5, mWith / 6, paint);
//还原设置
paint.setColor(Color.BLACK);
}
最后上色,留出眼白,点上高光比较可爱,如图所示:
这里的眼白也是用圆绘制,具体代码如下所示:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制右眼珠
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(mWith / 3, -mHight * 3 / 7, mWith / 20, paint);
//还原设置
paint.setStyle(Paint.Style.STROKE);
//绘制左眼珠
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(-mWith / 3, -mHight * 3 / 7, mWith / 20, paint);
//还原设置
paint.setStyle(Paint.Style.STROKE);
}
7、最后绘制冰墩墩的鼻子和嘴巴,如图所示:
这里鼻子就是一个圆,嘴巴用到了二阶贝塞尔曲线和点,这也是比较简单的,具体代码如下所示:
private Path mPathMouse = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制鼻子
paint.setStyle(Paint.Style.FILL);
//绘制圆
canvas.drawCircle(0, -mHight / 7, mWith / 20, paint);
//绘制嘴巴
paint.setStyle(Paint.Style.STROKE);
//移动起点
mPathMouse.moveTo(mWith / 4, 0);
mPathMouse.quadTo(0, mHight / 6, -mWith / 4, 0);
canvas.drawPath(mPathMouse, paint);
//绘制嘴巴两边的点
canvas.drawCircle(mWith / 4, 0, 1, paint);
canvas.drawCircle(-mWith / 4, 0, 1, paint);
}
到这里自定义的部分就绘制完成啦。
这里就不搞动画绘制了,需要的童鞋自己弄哈,接下来就是使用了,这还是比较简单的,如下所示:
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
android:layout_width="match_parent"
android:layout_height="match_parent" />
需要源码的童鞋【龙旋】公众号后台回复关键字:冰墩墩,注意这个读墩(dun),别回复错关键字哈。
到这里就搞定啦。