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),别回复错关键字哈。


到这里就搞定啦。

浏览 37
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报