关于android:感受一波Android自定义view实现超萌动感小炸弹

5次阅读

共计 6273 个字符,预计需要花费 16 分钟才能阅读完成。

老规矩,咱们先来看看效果图!

再来看 android 的实现成果。

上面咱们和自定义 view 实现超萌动感天气小太阳一样,开始解析动画!(没看过天气小太阳的敌人能够先去看天气小太阳,有些天气小太阳讲过的套路将不再讲,同时须要把握 path、camera、贝塞尔曲线等,不然局部代码可能会引起不适)。

咱们先把动态 view 绘制进去,而后再实现动画,Let’s go。

1. 地板

能够看到地板其实就是一条直线。而后两头两个缺口。这要个么实现呢?看到小太阳的小伙伴可能都会说,这很简略。只有画一线直线而后笼罩两个白的区间就能够了。确实这能够实现,然而仔细观察能够发现下方的缺口是两个半圆加矩形实现的,这样的话就有点麻烦,而且不不便缺口地位的挪动。那有什么简略的办法呢?有,那就是应用 Path 进行绘画一条直线,而后通过设置圆笔头,再设置 DashPathEffect(实现虚线,一段画,一段不画的成果,能够自在管制各段长度)来实现距离(本 view 的缺口都是应用此个性实现的,不相熟的小伙伴能够去看一下),代码如下:

float[] groundEffectFloat=new float[]  {bombLineWidth/4,bombLineWidth/2+bombLineWidth,bombLineWidth*2,bombLineWidth/3*2+bombLineWidth,getMeasuredWidth(),0};// 设置画与不画所占长度
        groundDashPathEffect=new DashPathEffect(groundEffectFloat,0);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(bombLineColor);
        mPaint.setPathEffect(groundDashPathEffect);// 设置虚线成果
        mPath.reset();
        mPath.moveTo(bombLineWidth/2,getMeasuredHeight()-bombLineWidth/2);
        mPath.lineTo(getMeasuredWidth()-bombLineWidth/2,getMeasuredHeight()-  
        bombLineWidth/2);
        canvas.drawPath(mPath,mPaint);

2. 身材的边框

认真一看!聪慧的你肯定会说太简略了,这不就是一个圆而后再用 DashPathEffect 实现缺口不就能够了!!嗯,对,就是这样的。间接放代码:

mPaint.setPathEffect(bodyDashPathEffect);
        mPaint.setColor(bombLineColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius,   
        Path.Direction.CW);
        canvas.drawPath(mPath,mPaint);
        canvas.restore();

简略!简略的不能再简略了,上面看身材

3. 身材

能够发现身材其实也就是一个圆,而后加上左上角的高光。那么高光是怎么实现的呢?

三个点的高光,很简略的,用 Path 画弧,而后应用 DashPathEffect 成果,完满。

那么另一个高光呢?看图。

能够看到就是条圆弧和一个门路合成的,而后裁剪放弃圆内。门路的造成就是取弧度的两个点,而后用贝塞尔曲线进行绘制,控制点位于弧度中分线中(下图红点)。

代码如下:(局部代码,左上角高光的,其它的请查看源码)

       // 左上角的光边
        mPaint.setPathEffect(null);
        mRectF.set(bombCenterX-bodyRadius+bombLineWidth/2,bombCenterY-bodyRadius+bombLineWidth/2
        ,bombCenterX+bodyRadius-bombLineWidth/2,getMeasuredHeight()-bombLineWidth-bombLineWidth/2);
        canvas.drawArc(mRectF,160,100,false,mPaint);        // 拼接光边
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2, Path.Direction.CCW);
        canvas.save();
        canvas.clipPath(mPath);// 裁剪圆内

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(lightColor);
        canvas.drawPath(mBodyLightPath,mPaint);
        canvas.restore();

4. 脸

大家能够看到,好你有点简单的,其实还好。这里是因为应用了 Z 轴旋转,看起来有点简单,那咱们移到两头。

如同简略了,眼睛和酒窝简略,4 个圆!!嘴巴,这个。。。这个如同有点恶心啊。其实不然,看图。

其实就是一个圆而后再加上一个门路图就能够实现,红点示意的是控制点。空心点示意节点。仔细的敌人可能发现,不对啊。舌头上面不全是红的,和嘴巴是离开的。这里只须要把嘴巴按比例放大,而后和嘴巴做个 Xfermode 就能够了。局部代码:

// 画舌头 圆和嘴巴的缩放相交,mpath 是嘴巴的门路
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        canvas.drawPath(mPath,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mPaint.setColor(Color.parseColor("#f34671"));
        canvas.drawCircle(bombCenterX,mouthY+(mouthMaxY-mouthY)/8+bodyRadius/(5-1.4f*mouthOffsetPercent),bodyRadius/5,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.scale(0.8f,0.8f,bombCenterX,(mouthMaxY+mouthY)/2);
        canvas.drawPath(mPath,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);

5. 脸上的暗影(不晓得叫什么,临时称为暗影遮罩)

一看,个别坏事的小伙伴说,你不会又让我用贝塞尔曲线画吧!这个不好找啊!!沉着沉着,这个实现如下:

如此简略,两个圆取红圆未相交的局部。

// 两个圆相交产生暗影
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(bombShadowColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        canvas.translate(-bodyRadius/5,-bodyRadius/5);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);

6. 头

小伙伴又要说了。这个不好画,不好画!!沉着沉着。这个其实更简略。只有把头放在身材的前面一层就能够了。看图:

代码:

太简略,我不想贴了,伪装我是代码

7. 引线

这个引线,其实也就是一线曲线,贝塞尔曲线持续上场(不解释,不懂的请面壁去)。

8. 爆炸成果

简略的不太再简略了,4 个圆,半径从大到小画,两头而后挖空。so easy!!

int save = canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);        float distance = maxBlastCircleRadius/12;        // 画圆
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius,mPaint);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance,mPaint);
        mPaint.setColor(bombLineColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*2,mPaint);
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*3,mPaint);        // 掏空
        if (blastCircleRadiusPercent >0.65) {mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            canvas.drawCircle(bombCenterX, circleY, currentBlastCircleRadius - maxBlastCircleRadius * 0.65f, mPaint);
            mPaint.setXfermode(null);
        }
        canvas.restoreToCount(save);

到这里,咱们曾经实现了一半,那就是小炸弹的显示,当初到了动画的工夫了!再次出场

9. 脸左右挪动动画

能够看到左右挪动,在挪动的工夫而后咱们只须要在画脸的工夫加一个偏移,而后在挪动的过程中,会发现脸会绕炸弹身材的核心旋转。所以代码如下

canvas.save();
        mCamera.save();
        mCamera.rotate(bombTBRotate,0,-bombLRRotate/3);
        mMatrix.reset();
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        mMatrix.preTranslate(-bombCenterX,-(bombCenterY));
        mMatrix.postTranslate(bombCenterX,bombCenterY);
        mMatrix.postTranslate(faceLROffset,faceTBOffset);
        canvas.setMatrix(mMatrix);

应用 camera,进行 z 轴的旋转,而后再进行translate 左右挪动,而后应用 valueanimator 动画对变偏移进行设置,搞定!在挪动过程中,能够发现眼睛有眯下的成果。这个很简略,能够把眼睛用椭圆来实现,放弃宽度不变,扭转高度就能够了。

10. 身材头部引线左右旋转

这个就更简略了,只须要在画之前用 camera 旋转变换获取 martix,而后对 canvas 进行变换。

11. 脸部高低挪动

首先和脸部左右挪动一样,应用 matrix.translate 进行高低挪动。眼睛的变换也一样。前面的眼睛放大成果,就是在变成圆的眼睛的时候,放大圆的半径。
嘴巴的变换就绝对比较复杂!看图,高能预警,我也不晓得我讲不讲得分明!!!!

这是方才画嘴巴的图!!!嘴巴动画有两个局部!!(以下语句可能会引起不适)

  • 第一局部嘴角往两边挪动,嘴巴变扁。这里咱们须要把 ab 两点用属性动画往两边挪动(两边的拐角点同样挪动),c 点往上方挪动,而后回到原始地位。
  • 第二局部是 ab 两点往两头聚拢,直到 ab 重合,同时 ab 两点往上移,de 的控制点同时拉长,直到造成一个椭圆。

不了解!!不了解再好好设想一下,空间设想能力。要是还想不懂,那就算了。毕竟平时用得不多。

12. 炸弹引线,点燃成果

炸弹引线成果同样分两个局部

  • 一个是引线变短,能够依据 PathMeasure,获取 Path 的比例 Path(比方 70% 的 Path),这样咱们就能够通过 ValueAnimator 用一个 0 到 1 的比例来绘制引线变短的成果
//mHeadLinePath 是引线的残缺 Path
       mPathMeasure.setPath(mHeadLinePath,false);
        mPath.reset();
      mPathMeasure.getSegment(0,mPathMeasure.getLength()*headLinePercent,mPath,true);// 依据比例获取对应比例的引线
        canvas.drawPath(mPath,mPaint);
  • 第二局部是点燃的成果。其实就是一个金色的实心圆,而后一个红色的圆边框,两头红色,三个圆按不同的速率和极限做放大放大动画(这里原设计还退出了变色的性能,金色圆会变色,能够用 ArgbEvaluator 实现)。

13. 爆炸动画

和引线动画类型,4 个圆做放大放大动画,只是到肯定的大小后,而后圆小漏空,并且漏空逐步放大。

原文链接:http://www.jianshu.com/p/a622…

文末

您的点赞珍藏就是对我最大的激励!
欢送关注我,分享 Android 干货,交换 Android 技术。
对文章有何见解,或者有何技术问题,欢送在评论区一起留言探讨!

正文完
 0