然而自己小时候底记忆几乎没了。光会使。

未懂得大家有无起这种感觉,童年的记得好像短失了。。。

这次未是格外钟动画,今天设享受的凡PathMeasure的玩法。

92年圣诞节落地,纯正90继。到目前为止也尚无多异常,但好小时候底记忆几乎从来不了。少有的记得停留在四驱兄弟、光会使这点儿总理动画达。。。

先是我们来回顾一下小时候吧~~90后满盈之记忆

曾几乎哪里时,“奥迪双钻,我之小伙伴”的声音萦绕耳畔。

无非会使

可极响的音响要——多莫克萨拉莫,闪光吧,你,光会使。

小儿接连幻想着祥和会变身,今天我们就是来用代码实现变身的首先步吧,动画绘制一个魔法阵magic_circle~~

仅会使受到三死魔鬼法阵

magic_circle.jpg

哈哈哈。看了仅仅会使却尚无团结画了就三独魔法阵的,有么?

静态图片于容易,我们用 Path 设置好路,然后还 canvas.drawPath
即可,但是静态的也太 low 了好几,我们设受它动起来。

已几乎何时,小学的教科书上处处都是六角形。

落实力量:

本人怀念,能够当初中的率先次于几何考试会将到100分是不是跟小学一直写者起提到呢?

magic_circle1.gif

脚真正的光会使出场!

功能看罢了,不会见刻画的童鞋肯定已经懵逼了,会的童鞋可以外出左拐了,因为实现实在太简单。

才会使魔法阵

吓了,在始发撸代码之前,我们事先来学一个类
PathMeasure。我们的只有会要者阵就是凡基于此类似的星星独点子撸出来的。

独自会使出场阶段

PathMeasure

夫仿佛的 class 注释就一个版权说明,酱紫~

Copyright.png

踏马的,Google
工程师还偷懒了,注释都无写。。。幸好这看似才生一百几近推行代码,那我们即便和好拘留吧

出场了

Public constructors

  • PathMeasure 创建一个缺损的 pathmeasure 对象
  • PathMeasure(Path path,boolean forceClosed)创建一个牵动 path 参数的
    PathMeasure,forceClosed控制 path 是否自动关闭

正面图

Public methods

  • getLength() 返回时 Path 的总长度。
  • getMatrix(float distance, Matrix matrix, int flags)
  • getPosTan(float distance, float[] pos, float[]
    tan)获取distance长度的 point 值给 pos,point 点的正切值给 tan。
  • getSegment(float startD, float stopD, Path dst, boolean
    startWithMoveTo) 获取 path 的一个局部,即startD到 stopD
    的线条,辅助给 dst。
  • isClosed() 是否自动关闭
  • nextContour() 移动及下一致长达曲线。如果 path
    中寓不总是的线条,getLength、getPosTan等办法的会于率先漫漫线及运行,需要运用这个主意跳到第二久线
  • setPath(Path path, boolean forceClosed)

大凡免是死粗略,就如此几只章程,现在去画光能而者阵来思路了么~~
搭下去为方便大家清楚,我们再来概括回顾一下 path 的
api,因为静态的单独会使者阵是用 path 去绘制的。

着重角色

Path

方法名 作用
moveTo 移动到指定点
setLastPoint 重新设置当前 Path 的最后一个点,如果当前 Path 为空,则等同上个方法
lineTo 添加当前点到一个指定点的直线
close 连接当前点到起点,形成闭合路径
addRect、addRoundRect、addOval、addCircle、addPath、addArc、arcTo 添加各种图形
isEmpty 是否为空
isRect 是否为矩形
set 用一个新的 path 替换
offset 对当前的路径进行偏移,不会影响后续操作
quadTo、cubicTo 贝塞尔曲线
rMoveTo、rLineTo、rQuaTo、rCubicTo 带 r 的是基于当前点的偏移量,不带 r 基于坐标原点
setFillType、getFillType 设置填充模式
transform 矩阵变换

即便如此概括回顾一下咔嚓,具体玩法可以参照 Hencoder 的 bolg【HenCoder
Android 开发进阶: 自定义 View 1-1
绘制基础】

就会使三小兄弟

动画片拆解

吓了,准备工作形成,我们开始撸代码


第一步,绘制静态的特会如者阵

第一绘制两独健全,然后就是是当中的六角星(其实仔细看即是个别独三角形)。
且是甚粗略的道,同学等动手去写的时光或会见碰到一个如此的题材,就是三角形的老三只点未好取。其实深简单,直接当圆上取0,1/3,2/3长度的接触即可,刚刚我们不是说了
PathMeasure 的方法么,用getPosTan就足以兑现。

公还记得这些呢

  • 吉布卡伊布利斯
  • 雅鹿里啪啪
  • 哈利路球球哈呀呀
  • 。。。

欢迎留言及自家交流~


自身是闫大伯,一独看正在只有会使长大的野生程序猿

其次步,让仅仅会使阵动起来

此我们把这个动画效果分成三只级次吧。

  • 第一级,绘制两独健全

step_1.gif

要达到图所示,这里少独周是日益绘制出来的, 圆的 path
很容易绘制出,这里我不怕非开口了,然后PathMeasure的getLength可以赢得 path
总的尺寸,getSegment可以获取有点交有点的 Path。因此一个
ValueAnimator 就足以化解从0到100%长的进程,具体落实看后边的代码。
下一场问题来了,path画出来的健全的起点于哪里?怎么控制两只健全开始绘制的角度不平等。有同学可能想到了兜90°再打第二单全面,当然这种措施是好兑现之,但是由后面的三角形也急需旋转,这里我们尽管毫无
path 画圆了,用 path 添加一个恰好方形 Rect
的弧形也是一个到家,然后我们的拱形可以决定初步之角度,弧度。
然后成这样了

erroe_1.gif

WTF?角度怎么怪了,我明确设置的开始角度的呀

innerCircle.addArc(innerRect, 150, -360);
outerCircle.addArc(outerRect, 60, -360);

终极来个大牛说您的圆变成闭环了,PathMeasure
找不至开接触,用了默认的。你把360度过变更化359.9于他莫是一个闭环的完美就实行了。

  • 次品,两单点当圆里面弹射

step_2.gif

圈起好像还要干什么碰撞反弹之类的转业,一合高科技的榜样,其实不是的。

轨道就是零星个三角,怎么为有限长长的线就三角形走呢,而且走之时节还要不段变化长度。

刚巧率先步我们因而 ValueAnimator 来控制一个圆从0到100%的过程,

pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath,
true);

随地截取起点到*%长度的 path 赋值给drawPath。

于里是于起点开始截取,那么我们无起起点开始截取,从目前触及隔壁开始截取不就是实施了吧,哈哈哈哈~so
easy

float stopD = distance * pathMeasure.getLength();
float startD = stopD – (0.5f – Math.abs(0.5f – distance)) * 200;
pathMeasure.getSegment(startD, stopD, drawPath, true);
酱紫~~

  • 老三等绘制两只三角

step_3.gif

实际简单个三角就是亚步的动轨迹,也是身为直接用第阶段的 Path
即可,然后再次用第一阶段同的方式就可实现力量。

代码实现

public class GranzortView extends View {

private Paint paint;

private Path innerCircle;//内圆 path
private Path outerCircle;//外圆 path
private Path trangle1;//第一个三角形的 Path
private Path trangle2;//第二个三角形的 Path
private Path drawPath;//用于截取路径的 Path

private PathMeasure pathMeasure;

private float mViewWidth;
private float mViewHeight;

private long duration = 3000;
private ValueAnimator valueAnimator;

private Handler mHanlder;

private float distance;//当前动画执行的百分比取值为0-1
private ValueAnimator.AnimatorUpdateListener animatorUpdateListener;
private Animator.AnimatorListener animatorListener;

private State mCurrentState = State.CIRCLE_STATE;

//三个阶段的枚举
private enum State {
    CIRCLE_STATE,
    TRANGLE_STATE,
    FINISH_STATE
}

public GranzortView(Context context) {
    this(context, null);
}

public GranzortView(Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
}

public GranzortView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mViewWidth = w;
    mViewHeight = h;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(getResources().getColor(R.color.colorPrimary));
    canvas.save();
    canvas.translate(mViewWidth / 2, mViewHeight / 2);
    switch (mCurrentState) {
        case CIRCLE_STATE:
            drawPath.reset();
            pathMeasure.setPath(innerCircle, false);
            pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
            canvas.drawPath(drawPath, paint);
            pathMeasure.setPath(outerCircle, false);
            drawPath.reset();
            pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
            canvas.drawPath(drawPath, paint);
            break;
        case TRANGLE_STATE:
            canvas.drawPath(innerCircle, paint);
            canvas.drawPath(outerCircle, paint);
            drawPath.reset();
            pathMeasure.setPath(trangle1, false);
            float stopD = distance * pathMeasure.getLength();
            float startD = stopD - (0.5f - Math.abs(0.5f - distance)) * 200;
            pathMeasure.getSegment(startD, stopD, drawPath, true);
            canvas.drawPath(drawPath, paint);
            drawPath.reset();
            pathMeasure.setPath(trangle2, false);
            pathMeasure.getSegment(startD, stopD, drawPath, true);
            canvas.drawPath(drawPath, paint);
            break;
        case FINISH_STATE:
            canvas.drawPath(innerCircle, paint);
            canvas.drawPath(outerCircle, paint);
            drawPath.reset();
            pathMeasure.setPath(trangle1, false);
            pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
            canvas.drawPath(drawPath, paint);
            drawPath.reset();
            pathMeasure.setPath(trangle2, false);
            pathMeasure.getSegment(0, distance * pathMeasure.getLength(), drawPath, true);
            canvas.drawPath(drawPath, paint);
            break;

    }

    canvas.restore();

}

private void init() {

    initPaint();

    initPath();

    initHandler();

    initAnimatorListener();

    initAnimator();

    mCurrentState = State.CIRCLE_STATE;
    valueAnimator.start();

}

private void initHandler() {
    mHanlder = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (mCurrentState) {
                case CIRCLE_STATE:
                    mCurrentState = State.TRANGLE_STATE;
                    valueAnimator.start();
                    break;
                case TRANGLE_STATE:
                    mCurrentState = State.FINISH_STATE;
                    valueAnimator.start();
                    break;
            }
        }
    };
}

private void initAnimatorListener() {
    animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            distance = (float) animation.getAnimatedValue();
            invalidate();
        }
    };

    animatorListener = new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
            Log.e("star:",mCurrentState+"_");
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            Log.e("end:",mCurrentState+"_");
            mHanlder.sendEmptyMessage(0);
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    };
}

private void initAnimator() {
    valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(duration);

    valueAnimator.addUpdateListener(animatorUpdateListener);

    valueAnimator.addListener(animatorListener);
}

private void initPath() {
    innerCircle = new Path();
    outerCircle = new Path();
    trangle1 = new Path();
    trangle2 = new Path();
    drawPath = new Path();

    pathMeasure = new PathMeasure();

    RectF innerRect = new RectF(-220, -220, 220, 220);
    RectF outerRect = new RectF(-280, -280, 280, 280);
    innerCircle.addArc(innerRect, 150, -359.9F);     // 不能取360f,否则可能造成测量到的值不准确
    outerCircle.addArc(outerRect, 60, -359.9F);

    pathMeasure.setPath(innerCircle, false);

    float[] pos = new float[2];
    pathMeasure.getPosTan(0, pos, null);        // 获取开始位置的坐标
    trangle1.moveTo(pos[0], pos[1]);
    pathMeasure.getPosTan((1f / 3f) * pathMeasure.getLength(), pos, null);
    System.out.println("pos : " + pos[0] + "  " + pos[1]);

    trangle1.lineTo(pos[0], pos[1]);
    pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
    trangle1.lineTo(pos[0], pos[1]);
    trangle1.close();

    pathMeasure.getPosTan((2f / 3f) * pathMeasure.getLength(), pos, null);
    Matrix matrix = new Matrix();
    matrix.postRotate(-180);
    trangle1.transform(matrix, trangle2);
}

private void initPaint() {
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.WHITE);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeWidth(10);
    paint.setStrokeCap(Paint.Cap.ROUND);
    paint.setStrokeJoin(Paint.Join.BEVEL);
    paint.setShadowLayer(15, 0, 0, Color.WHITE);//白色光影效果
}}

好,光会而者阵就了,离变身成为单纯会如者单独差喊口号及变身动作了,加油吧,有要之程序员。
末段,感觉微微伙伴Moo_Night分享的独自会要者阵。

相关文章