效果
前言
还不够完善,只做学习之用。
原理
不停的重绘,即不停的改变点的位置以及透明度,不停的invaldate。所以,关键就在于根据动画执行时间确定点的位置以及透明度。
首先定义,一个点飞入飞出所用时间为duration,点的数量为pointNum,那么可知pointNum*duration*2为一个周期。我们把动画执行时间对周期取余,自然就可得到循环重复效果。
在一个周期里面划分为pointNum*2步,前pointNum步是各个点依次飞入,后pointNum步是各个点依次飞出。根据动画当前执行到哪一步去计算点的位置。
把点的位置又分为三个部分——enterX, transX, exitX,分别代表飞入距离、平移距离、飞出距离。根据动画当前执行到哪一步分别计算。在计算飞入距离和飞出距离时利用补差器去让运动更加平滑。
源码
package com.example.myronlg.windowsstyleloadingdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by myron.lg on 2015/7/9.
*/
public class LoadingView extends View {
/**
* A list that contains the cx of each point.
*/
private List<Float> cx;
/**
* The cy of every point is the same.
*/
private float cy;
/**
* The radius of every point is the same.
*/
private float radius;
/**
* The paints that used to draw each point.
*/
private List<Paint> paints;
/**
* The length that point transfer to enter and exit.
*/
private float dx;
private List<Float> dxs;
/**
* The offset between each point.
*/
private float offset;
/**
* Used in animation.
*/
private long startMillis = -1;
/**
* Used to make translation more smooth
*/
private Interpolator enterInterpolator, exitInterpolator;
/**
* The time one point enter or exit
*/
private long duration;
/**
* The moving velocity of the point which is not entering or exiting
*/
private float v = 0.04F;
/**
* The number of points
*/
private int pointNum;
private float cxOffest;
public LoadingView(Context context) {
super(context);
init();
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
pointNum = 4;
cx = new ArrayList<>(Collections.nCopies(pointNum, 0.0F));
Paint paint = new Paint();
paint.setDither(true);
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#00BCD4"));
paint.setAlpha(0);
paints = new ArrayList<>();
for (int i = 0; i < pointNum; i++) {
paints.add(new Paint(paint));
}
enterInterpolator = new DecelerateInterpolator(1.5F);
exitInterpolator = new AccelerateInterpolator(2.0F);
duration = 600;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
initSize();
}
private void initSize(){
cy = getMeasuredHeight() / 2;
radius = getMeasuredHeight() / 3;
dx = getMeasuredWidth() / 3;
cxOffest = (getMeasuredWidth() - 2*dx - v*duration*3) * 0.5F;
offset = radius * 3;
}
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < cx.size(); i++) {
canvas.drawCircle(cx.get(i), cy, radius, paints.get(i));
}
update();
}
private void update(){
long currentMillis = System.currentTimeMillis();
if (startMillis == -1){
startMillis = currentMillis;
}
// (pointNum * 2 * duration) is a cycle
long passMills = (currentMillis - startMillis) % (pointNum * 2 * duration);
// update each point's cx and alpha
for (int i = 0; i < cx.size(); i++) {
long step = passMills / duration;
float animationFraction = (passMills % duration) * 1.0F / duration;
float enterX = 0;
float transX = 0;
float exitX = 0;
if (step < 4) { // entering half
if (i < step) {
enterX = dx - i*offset + i*duration*v;
transX = (passMills - (i + 1) * duration) * v;
exitX = 0;
paints.get(i).setAlpha(255);
} else if (i == step) {
float interpolatedFraction = enterInterpolator.getInterpolation(animationFraction);
enterX = interpolatedFraction*dx - i*offset + i*duration*v;
transX = 0;
exitX = 0;
paints.get(i).setAlpha((int) (255*interpolatedFraction));
} else {
enterX = 0;
transX = 0;
exitX =0;
paints.get(i).setAlpha(0);
}
} else { // exiting half
if (i < step-4){
enterX = dx - i*offset + i*duration*v;
transX = (passMills - (i + 1) * duration) * v;
exitX = dx;
paints.get(i).setAlpha(0);
} else if (i == step-4){
float interpolatedFraction = exitInterpolator.getInterpolation(animationFraction);
enterX = dx - i*offset + i*duration*v;
transX = (passMills - (i + 1) * duration) * v;
exitX = interpolatedFraction * dx;
paints.get(i).setAlpha((int) (255*(1-interpolatedFraction)));
} else {
enterX = dx - i*offset + i*duration*v;
transX = (passMills - (i + 1) * duration) * v;
exitX = 0;
paints.get(i).setAlpha(255);
}
}
cx.set(i, cxOffest + enterX + transX + exitX);
}
invalidate();
}
}