Android仿视频加载旋转小球动画效果的实例代码
先上个效果图,以免大家跑错地了。
嗯,除了只能录三秒,其他没啥问题。
下面分析一下怎么实现上面这个效果。
理性分析后我们可以看到是几个小球绕着一个圆进行运动,那这里面的重点我们看看什么。
绘制五个球,没什么难度,让球绕圆进行运动,这个好像我们没有见到是怎么去实现了,那下就说这个。
从本质上看,球绕圆运动,其实我们可以看作是一个物体绕指定的路劲运动,那我们就有下面几个东西需要说一下:
1:Path
2:ValueAnimator
3:PathMeasure
前两个大家应该都见过,一个是路径,就是可以自己绘制路线的一个工具,一个是动画,用来指定物体运动的工具,那第三个是一个关于测量路径的类。
下面说说PathMeasure的用法。
首先是初始化:
pathMeasure = new PathMeasure(path, false);
两个参数第一个,第一个就是我们需要用到的路径,第二个参数意思就是这个以路径头尾是否相连来计算结果,通常我们就写false就行,不会有问题。
然后是用法:
private float[] mCurrentPositionOne = new float[2]; float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); 我们可以看见把一个二维数组放到了getPosTan这个方法里面,然后还有一个animation,这里的animation来自哪里呢?来自这里:valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 获取当前点坐标封装到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } });
看见没,是动画的监听里面来的,getPosTan的最后一个参数通常也就写null就行了,那么这整个一行的代码意思就是当动画发生了变化,就执行这行代码,然后这行代码会把这个时间点的路径上的坐标赋值给mCurrentPositionOne。
那我们获取到看这个路径上的坐标点怎么办呢?
立马用来ondraw里面啊,我的小球此时就可以根据这个坐标点去绘制自己的位置,这个的话,当动画开始时,小球就会不断接受新的坐标,然后不断重绘,最终产生旋转小球的效果。
我先把属性动画的代码贴出来:
if (valueAnimatorOne == null) { valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorOne.setDuration(800); // 减速插值器 valueAnimatorOne.setInterpolator(new DecelerateInterpolator()); valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 获取当前点坐标封装到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } }); valueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateOne = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateOne = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorOne.start();
我写了个800,也就是动画的维持时间,但是我们发现有啊后几个小球,所以我们需要绘制好几个小球,然后给他们不同的动画,为什么呢?因为动画都一样,小球就叠加在一起了,我们就只能看见一个球了。
说到这里的话,我们的目标算时完成了,具体的操作,大家参考以下代码,或者去:android自定义View索引
里面动画的demo进行下载,大家随意,下面给出代码:
/** * 仿视频加载动画,旋转的蓝色小球 */ public class RotaryBall extends View { private Path rotationPath; private float radius; private Paint circlePaintOne; private PathMeasure pathMeasure; private int finishAnimateOne = 0; // 用来判断当前动画有没有开始 private int finishAnimateTwo = 0; // 用来判断当前动画有没有开始 private int finishAnimateThree = 0; // 用来判断当前动画有没有开始 private int finishAnimateFour = 0; // 用来判断当前动画有没有开始 private int finishAnimateFive = 0; // 用来判断当前动画有没有开始 private Handler handler; private float[] mCurrentPositionOne = new float[2]; private float[] mCurrentPositionTwo = new float[2]; private float[] mCurrentPositionThree = new float[2]; private float[] mCurrentPositionFour = new float[2]; private float[] mCurrentPositionFive = new float[2]; private ValueAnimator valueAnimatorOne = null; private ValueAnimator valueAnimatorTwo = null; private ValueAnimator valueAnimatorThree = null; private ValueAnimator valueAnimatorFour = null; private ValueAnimator valueAnimatorFive = null; private int currentStatus = -1; //-1表示第一次运行,0表示动画结束或者没开始,1表示正在运动中 private boolean animateOrNot = true; //用来决定是否开启动画 public RotaryBall(Context context) { super(context); initData(); } public RotaryBall(Context context, AttributeSet attrs) { super(context, attrs); initData(); } private void initData() { rotationPath = new Path(); circlePaintOne = new Paint(); circlePaintOne.setColor(Color.BLUE); circlePaintOne.setAntiAlias(true); handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 4: if (finishAnimateOne == 0) { startAnimatorOne(); } if (finishAnimateTwo == 0) { startAnimatorTwo(); } if (finishAnimateThree == 0) { startAnimatorThree(); } if (finishAnimateFour == 0) { startAnimatorFour(); } if (finishAnimateFive == 0) { startAnimatorFive(); } currentStatus = 0; } } }; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); radius = getMeasuredWidth() / 2; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // rotationPath.addCircle(radius, radius, radius - 10, CW); rotationPath.moveTo(radius, 0 + 10); rotationPath.cubicTo(radius, 0 + 10, radius * 2 - 10, 0 + 10, radius * 2 - 10, radius); rotationPath.cubicTo(radius * 2 - 10, radius, radius * 2 - 10, radius * 2 - 10, radius, radius * 2 - 10); rotationPath.cubicTo(radius, radius * 2 - 10, 0 + 10, radius * 2 - 10, 0 + 10, radius); rotationPath.cubicTo(0 + 10, radius, 0 + 10, 0 + 10, radius, 0 + 10); rotationPath.close(); pathMeasure = new PathMeasure(rotationPath, false); //下面绘制不同半径的小圆 canvas.drawCircle(mCurrentPositionOne[0], mCurrentPositionOne[1], 10, circlePaintOne); canvas.drawCircle(mCurrentPositionTwo[0], mCurrentPositionTwo[1], 9, circlePaintOne); canvas.drawCircle(mCurrentPositionThree[0], mCurrentPositionThree[1], 7, circlePaintOne); canvas.drawCircle(mCurrentPositionFour[0], mCurrentPositionFour[1], 5, circlePaintOne); canvas.drawCircle(mCurrentPositionFive[0], mCurrentPositionFive[1], 3, circlePaintOne); if (currentStatus == -1) { Message message = new Message(); message.what = 4; handler.sendMessage(message); } if (animateOrNot) { if (currentStatus == 0) { currentStatus = 1; new Thread() { //用线程来统一五个圆的周期 @Override public void run() { super.run(); try { Log.d("thread", "thread"); Thread.sleep(1600); Message message = new Message(); message.what = 4; handler.sendMessage(message); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } } } //供外部调用,开始动画 public void startAnimate() { if (!animateOrNot) { animateOrNot = true; currentStatus = -1; invalidate(); } } //供外部调用,停止动画 public void stopAnimate() { if (animateOrNot) { animateOrNot = false; } } //界面被销毁 @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimate(); clearAllAnimation(); } //清除所有动画效果 private void clearAllAnimation() { if (valueAnimatorOne != null){ if (valueAnimatorOne.isRunning()){ valueAnimatorOne.cancel(); } valueAnimatorOne.removeAllUpdateListeners(); valueAnimatorOne = null; } if (valueAnimatorTwo != null){ if (valueAnimatorTwo.isRunning()){ valueAnimatorTwo.cancel(); } valueAnimatorTwo.removeAllUpdateListeners(); valueAnimatorTwo = null; } if (valueAnimatorThree != null){ if (valueAnimatorThree.isRunning()){ valueAnimatorThree.cancel(); } valueAnimatorThree.removeAllUpdateListeners(); valueAnimatorThree = null; } if (valueAnimatorFour != null){ if (valueAnimatorFour.isRunning()){ valueAnimatorFour.cancel(); } valueAnimatorFour.removeAllUpdateListeners(); valueAnimatorFour = null; } if (valueAnimatorFive != null){ if (valueAnimatorFive.isRunning()){ valueAnimatorFive.cancel(); } valueAnimatorFive.removeAllUpdateListeners(); valueAnimatorFive = null; } } //开始第一个小球的动画 private void startAnimatorOne() { if (valueAnimatorOne == null) { Log.d("valueAnimatorOne", "valueAnimatorOne"); valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorOne.setDuration(800); // 减速插值器 valueAnimatorOne.setInterpolator(new DecelerateInterpolator()); valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 获取当前点坐标封装到mCurrentPosition float value = (Float) animation.getAnimatedValue(); pathMeasure.getPosTan(value, mCurrentPositionOne, null); postInvalidate(); } }); valueAnimatorOne.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateOne = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateOne = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorOne.start(); } //开始第二个小球的动画 private void startAnimatorTwo() { if (valueAnimatorTwo == null) { valueAnimatorTwo = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorTwo.setDuration(1000); // 减速插值器 valueAnimatorTwo.setInterpolator(new DecelerateInterpolator()); valueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 获取当前点坐标封装到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionTwo, null); postInvalidate(); } }); valueAnimatorTwo.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateTwo = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateTwo = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorTwo.start(); } //开始第三个小球的动画 private void startAnimatorThree() { if (valueAnimatorThree == null) { valueAnimatorThree = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorThree.setDuration(1200); // 减速插值器 valueAnimatorThree.setInterpolator(new DecelerateInterpolator()); valueAnimatorThree.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 获取当前点坐标封装到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionThree, null); postInvalidate(); } }); valueAnimatorThree.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateThree = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateThree = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorThree.start(); } //开始第四个小球的动画 private void startAnimatorFour() { if (valueAnimatorFour == null) { valueAnimatorFour = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorFour.setDuration(1400); // 减速插值器 valueAnimatorFour.setInterpolator(new DecelerateInterpolator()); valueAnimatorFour.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 获取当前点坐标封装到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionFour, null); postInvalidate(); } }); valueAnimatorFour.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateFour = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateFour = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorFour.start(); } //开始第五个小球的动画 private void startAnimatorFive() { if (valueAnimatorFive == null) { valueAnimatorFive = ValueAnimator.ofFloat(0, pathMeasure.getLength()); valueAnimatorFive.setDuration(1600); // 减速插值器 valueAnimatorFive.setInterpolator(new DecelerateInterpolator()); valueAnimatorFive.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (Float) animation.getAnimatedValue(); // 获取当前点坐标封装到mCurrentPosition pathMeasure.getPosTan(value, mCurrentPositionFive, null); postInvalidate(); } }); valueAnimatorFive.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { finishAnimateFive = 1; } @Override public void onAnimationEnd(Animator animator) { finishAnimateFive = 0; } @Override public void onAnimationCancel(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } }); } valueAnimatorFive.start(); } }
总结
以上所述是小编给大家介绍的Android仿视频加载旋转小球动画实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!