效果图:
思路:
分为两种类型:
一种是两个球循环切换位置,另一种是四个球成对角移动,
1.初始化参数 : onMeasure方法计算控件的高和宽,然后初始化球的直径(四个球一样大),初始化四个球 或 两个球的中心点坐标,再计算球间的距离
再初始化ValueAnimator,和四个球的画笔。
2.动画效果:通过ValueAnimator的updateListener获取0.0F---1.0F之间的变化值value,根据value值不断去ondraw,来绘制每个球的位置。注意两个球的时候上下位置会发生变化,需要做个判断。
public class BallProgressView extends View {
private int style; //
public static final int TWO_BALL_STYLE = 2;
public static final int FOUR_BALL_STYLE = 4;
private int MAX_BALL_SIZE; //球的最大值
private int MIN_BALL_SIZE; //球的最小值
private int HEIGHT; //自己的高度
private int WIDTH; //自己的宽度
private float lengthOfTwoBall; //两个球的距离
private Paint firstBallPaint; //第一个球的画笔
private Paint secondBallPaint; //第二个球的画笔
private float firstBallSize; //第一个球的大小
private float secondBallSize; //第二个球的大小
private float firstBallX; //第一个球的中心点
private float firstBallY;
private float secondBallX; //第二个球的中心点
private float secondBallY;
private ValueAnimator valueAnimator; //动画
private float preValue; //之前的值
private float value; //现在的值
private boolean isFirstBallTop = true; //是否是第一个球最上面显示
private long duration;
private float coorBalls[][]; //四个球的坐标
private Paint thirdPaint;
private Paint fourthPaint;
public BallProgressView(Context context) {
super(context);
style = TWO_BALL_STYLE; //默认风格
}
public BallProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
style = TWO_BALL_STYLE; //默认风格
}
public BallProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
style = TWO_BALL_STYLE; //默认风格
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
WIDTH = getMeasuredWidth();
HEIGHT = getMeasuredHeight();
System.out.println("width = " + WIDTH + " height = " + HEIGHT);
MAX_BALL_SIZE = WIDTH/5;
MIN_BALL_SIZE = WIDTH/6;
init();
}
private void init() {
secondBallSize = firstBallSize = (MAX_BALL_SIZE + MIN_BALL_SIZE)/2;
lengthOfTwoBall = WIDTH - firstBallSize;
//两个球中心点坐标
firstBallX = MAX_BALL_SIZE/2;
firstBallY = secondBallY = HEIGHT/2;
secondBallX = WIDTH - firstBallX;
//四个球中心点坐标
coorBalls = new float[4][2];
coorBalls[0][0] = MAX_BALL_SIZE/2;
coorBalls[0][1] = MAX_BALL_SIZE/2;
coorBalls[1][0] = MAX_BALL_SIZE/2 + lengthOfTwoBall;
coorBalls[1][1] = MAX_BALL_SIZE/2;
coorBalls[2][0] = MAX_BALL_SIZE/2;
coorBalls[2][1] = MAX_BALL_SIZE/2 + lengthOfTwoBall;
coorBalls[3][0] = MAX_BALL_SIZE/2 + lengthOfTwoBall;
coorBalls[3][1] = MAX_BALL_SIZE/2 + lengthOfTwoBall;
initPaint();
duration = 1000;
initAnimator();
}
private void initPaint() {
firstBallPaint = new Paint();
firstBallPaint.setAntiAlias(true);
firstBallPaint.setColor(getResources().getColor(R.color.green_01));
secondBallPaint = new Paint();
secondBallPaint.setAntiAlias(true);
secondBallPaint.setColor(getResources().getColor(R.color.yellow));
thirdPaint = new Paint();
thirdPaint.setAntiAlias(true);
thirdPaint.setColor(getResources().getColor(R.color.blue_01));
fourthPaint = new Paint();
fourthPaint.setAntiAlias(true);
fourthPaint.setColor(getResources().getColor(R.color.red_01));
}
private void initAnimator() {
preValue = value = 0f;
valueAnimator = ValueAnimator.ofFloat(0f, 1f, 0f);
valueAnimator.setDuration(duration);
valueAnimator.setRepeatCount(-1);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
value = (Float) animation.getAnimatedValue();
if ((value - preValue) > 0)
isFirstBallTop = true;
else if ((value - preValue) < 0) {
isFirstBallTop = false;
}
preValue = value;
invalidate();
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
isFirstBallTop = true;
}
});
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if(style == TWO_BALL_STYLE) {
if (isFirstBallTop) {
canvas.drawCircle(secondBallX - (lengthOfTwoBall * value), secondBallY, (secondBallSize / 2), secondBallPaint);
canvas.drawCircle(firstBallX + (lengthOfTwoBall * value), firstBallY, (firstBallSize / 2), firstBallPaint);
} else {
canvas.drawCircle(firstBallX + (lengthOfTwoBall * value), firstBallY, (firstBallSize / 2), firstBallPaint);
canvas.drawCircle(secondBallX - (lengthOfTwoBall * value), secondBallY, (secondBallSize / 2), secondBallPaint);
}
}
if(style == FOUR_BALL_STYLE){
canvas.drawCircle(coorBalls[0][0] + (lengthOfTwoBall * value), coorBalls[0][1] + (lengthOfTwoBall * value), (firstBallSize / 2), firstBallPaint);
canvas.drawCircle(coorBalls[1][0] - (lengthOfTwoBall * value), coorBalls[1][1] + (lengthOfTwoBall * value), (secondBallSize / 2), secondBallPaint);
canvas.drawCircle(coorBalls[2][0] + (lengthOfTwoBall * value), coorBalls[2][1] - (lengthOfTwoBall * value), (firstBallSize / 2), thirdPaint);
canvas.drawCircle(coorBalls[3][0] - (lengthOfTwoBall * value), coorBalls[3][1] - (lengthOfTwoBall * value), (secondBallSize / 2), fourthPaint);
}
}
public void startAnimation() {
if(valueAnimator != null)
valueAnimator.start();
}
public void cancleAnimation(){
if(valueAnimator != null){
valueAnimator.cancel();
}
}
public void setBallColor(int firstBallColor, int secondBallColor){
firstBallPaint.setColor(firstBallColor);
secondBallPaint.setColor(secondBallColor);
invalidate();
}
public void setStyle(int style){
this.style = style;
}
public void setDuration(long duration){
this.duration = duration;
initAnimator();
}
}
调用方式:
ballProgressView = (BallProgressView)findViewById(R.id.activity_login_ball_progress);
ballProgressView.setStyle(BallProgressView.FOUR_BALL_STYLE);
ballProgressView.setDuration(1500);
ballProgressView.startAnimation();