View详解(7)-六边形加载动画

时间:2024-05-23 17:41:54

View详解(7)-六边形加载动画

前几篇中我们学习过了一些规则及不规则图形的绘制技巧,不知道大家理解是否到位,这篇文章中将继续前文主题,研究Path的基本应用,前文中我们已经基本了解了Path的构建,接下来我们来学习一种比较炫酷的动画方案-路径动画路径动画指的是某一图形或者控件按照固有路径进行绘制或者运动。下面展示的就是一个典型的路径动画:

View详解(7)-六边形加载动画

 

下面我们一起学习下如何实现该动画。


形成过程

按照惯有思路,我们观察动图,分析动画的构成,可以得到下图:

View详解(7)-六边形加载动画

 

六边形

两个六边形的绘制方法相信小伙伴们并不陌生吧,我们在第⑤篇中已经有基本思路了,基于已有思路我们可以建立下图坐标系

View详解(7)-六边形加载动画

 

同时那么我们可以得到线条运动轨迹为:

O-A-B-C-D-E-O-F-G-H-J-J-O

假设坐标原点O的坐标为:

O(mCenterX,mCenterY)

两个六边形的外接圆半径为:

mRadius

我们可以得到左边六边形外接圆圆心坐标为:

a = mCenterX - mRadius;
b = mCenterY

左边六边形圆上点坐标为:

x = a + mRadius * (float) Math.cos(Math.PI * M/ 180)
y = b + mRadius * (float) Math.sin(Math.PI * M/ 180)
(M为角度值,顺时针为正值,逆时针为负值)

同理我们可以得到右侧六边形的外接圆心坐标及圆上点坐标公式:

a = mCenterX + mRadius;
b = mCenterY ;

x = a + mRadius * (float) Math.cos(Math.PI * M/ 180)
y = b + mRadius * (float) Math.sin(Math.PI * M/ 180)
(M为角度值,顺时针为正值,逆时针为负值)

线条

移动的线条本质是获取了运动轨迹:

O-A-B-C-D-E-O-F-G-H-J-J-O

中的一部分,然后通过改变这部分在轨迹上的位置而形成的。

构造六边形Path

根据形成过程中的分析,我们可以构造两个六边形的Path对象如下:

mPath.reset();
//移动到View中心
mPath.moveTo(mCenterX, mCenterY);
//左侧六边形
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 60 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 60 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 120 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 120 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 180 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 180 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 240 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 240 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 300 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 300 / 180));
mPath.lineTo(mCenterX, mCenterY);
//右侧六边形
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 120 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 120 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 60 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 60 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 0 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 0 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 300 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 300 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 240 / 180),
    mCenterY + mRadius * (float) Math.sin(Math.PI * 240 / 180));
mPath.lineTo(mCenterX, mCenterY);

运行后可以看到下图就证明你已迈出了长征的一半。

View详解(7)-六边形加载动画

 

构造线条Path

如形成过程中分析,移动的线条本质上来源于六边形所组成的运动轨迹中的一段,在构造六边形Path中我们已经获得了运动轨迹,接下来就是从运动轨迹中获取一段固定距离了,那么那个类能实现这个功能呢?PathMeasure(主角上场了)

在使用PathMeasure类时,需要为其设置一个Path对象,可以通过构造传入,也可以通过setPath方法设置,PathMeasure类提供了如下公有方法:

View详解(7)-六边形加载动画

 


查看函数说明我们需要使用的自然是getSegment()函数,说明如下(其他方法后续文章中介绍):

/**
* startD 开始点距离起点的距离
* stopD  结束位置距离起点的位置
* dest   截取到的Path对象
**/
getSegment(float startD,float stopD,Path dest,boolean startWithMoveTo)

假设线条长度为20,那么我们从Path中截取刚开始的一段距离的代码如下:

mPathMeasure = new PathMeasure(mPath,false);
float length = mPathMeasure.getLength();
Path linePath = new Path();
linePath.reset();
//需要调用该方法重置Path,否则获取到的Path段不能被绘制出来
linePath.rLineTo(0,0);
mPathMeasure.getSegment(0,20,linePath,true);
canvas.drawPath(linePath,mLinePaint);

运行后效果:

View详解(7)-六边形加载动画

 

动画

已经绘制出来基本图形,那么线条动起来就简单了,直接控制getSegment函数的startD参数变化就可以了。代码如下:

  public void startAnimation(){
    mValueAnimator = ValueAnimator.ofFloat(0,1.0f);
    mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator animation) {
        mCurrentValue = (float) animation.getAnimatedValue();
        postInvalidate();
      }
    });
    mValueAnimator.setDuration(3000);
    mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
    mValueAnimator.setRepeatCount(-1);
    mValueAnimator.start();
  }
float length = mPathMeasure.getLength();
    Path linePath = new Path();
    linePath.reset();
    linePath.rLineTo(0,0);
    mPathMeasure.getSegment(length*mCurrentValue,length*mCurrentValue+mLineDistance,linePath,true);
    canvas.drawPath(linePath,mLinePaint);

到此,我们就完成了上面动画的绘制,不知道你们get到没,查看完整代码,请在后台回复2即可

View详解(7)-六边形加载动画