想要知道关于更多自定义View的实例,请参考:android自定义View索引
先上个效果图,以免大家跑错地了。
嗯,除了只能录三秒,其他没啥问题。
下面分析一下怎么实现上面这个效果。
理性分析后我们可以看到是几个小球绕着一个圆进行运动,那这里面的重点我们看看什么。
绘制五个球,没什么难度,让球绕圆进行运动,这个好像我们没有见到是怎么去实现了,那下就说这个。
从本质上看,球绕圆运动,其实我们可以看作是一个物体绕指定的路劲运动,那我们就有下面几个东西需要说一下:
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();
}
}复制代码
喜欢我的文章的,请点击关注我哦。万学冬的掘金