Android 自定義兩球旋轉動畫效果
阿新 • • 發佈:2019-01-23
自定義一個兩球旋轉動畫效果,可以用於載入時的動畫展示
先定義attrs屬性
<resources>
<declare-styleable name="BallRotationAnim">
<!--球最大半徑-->
<attr name="max_radius" format="dimension" />
<!--球最小半徑-->
<attr name="min_radius" format="dimension" />
<!--第一個球顏色-->
<attr name="one_color" format="color" />
<!--第二個球顏色-->
<attr name="two_color" format="color" />
<!--兩球之間間距-->
<attr name="distance" format="dimension" />
<!--動畫執行時間-->
<attr name="duration" format="integer" />
</declare-styleable>
</resources>
具體步驟都在註釋裡
package com.example.twoballrotationanim.widget;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import com.example.twoballrotationanim.R;
/**
* 兩個小球旋轉動畫
*/
public class BallRotationAnim extends View {
//預設最大半徑
private final static int DEFUALT_MAX_RADIUS = 25;
//預設最小半徑
private final static int DEFUALT_MIN_RADIUS = 15;
//預設兩個球心距離
private final static int DEFAULT_DISTANCE = 35;
//預設第一個球顏色
private final static int DEFAULT_ONE_BALL_COLOR = Color.argb(255, 247, 48, 46);
//預設第二個球顏色
private final static int DEFAULT_TWO_BALL_COLOR = Color.argb(255, 30, 199, 247);
//預設是動畫執行時間
private final static long DEFUALT_ANIMATOR_DURATION = 1000;
//當前畫筆
private Paint mPaint;
//當前球最大半徑
private float mMaxRadius = DEFUALT_MAX_RADIUS;
//當前球最小半徑
private float mMinRadius = DEFUALT_MIN_RADIUS;
//兩球心間隔距離
private float mDistance = DEFAULT_DISTANCE;
//動畫時間
private long mDuration = DEFUALT_ANIMATOR_DURATION;
//第一個球顏色
private int mOneColor = DEFAULT_ONE_BALL_COLOR;
//第二個球顏色
private int mTwoColor = DEFAULT_TWO_BALL_COLOR;
private Ball mOneBall;
private Ball mTwoBall;
private int mCenterX;
private int mCenterY;
private AnimatorSet animatorSet;
public BallRotationAnim(Context context) {
this(context, null);
}
public BallRotationAnim(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public BallRotationAnim(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BallRotationAnim);
mMaxRadius = typedArray.getDimension(R.styleable.BallRotationAnim_max_radius, DEFUALT_MAX_RADIUS);
mMinRadius = typedArray.getDimension(R.styleable.BallRotationAnim_min_radius, DEFUALT_MIN_RADIUS);
mOneColor = typedArray.getColor(R.styleable.BallRotationAnim_one_color, DEFAULT_ONE_BALL_COLOR);
mTwoColor = typedArray.getColor(R.styleable.BallRotationAnim_two_color, DEFAULT_TWO_BALL_COLOR);
mDistance = typedArray.getDimension(R.styleable.BallRotationAnim_distance, DEFAULT_DISTANCE);
mDuration = typedArray.getInt(R.styleable.BallRotationAnim_duration, (int) DEFUALT_ANIMATOR_DURATION);
typedArray.recycle();
mOneBall = new Ball();
mTwoBall = new Ball();
mOneBall.color = mOneColor;
mTwoBall.color = mTwoColor;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
initAnimator();
}
private void initAnimator() {
//球移動到中間的半徑
float centerRadius = (mMaxRadius + mMinRadius) * 0.5f;
//第一個球縮放動畫
ObjectAnimator oneScaleAnimator = ObjectAnimator.ofFloat(mOneBall, "radius", centerRadius, mMaxRadius, centerRadius, mMinRadius, centerRadius);
//無線迴圈
oneScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);
//第一個球移動動畫
ValueAnimator oneTranslationAnimtor = ValueAnimator.ofFloat(-1, 0, 1, 0, -1);
oneTranslationAnimtor.setRepeatCount(ValueAnimator.INFINITE);
oneTranslationAnimtor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
float x = mCenterX + mDistance * value;
mOneBall.centerX = x;
invalidate();
}
});
//第二個球縮放動畫
ObjectAnimator twoScaleAnimator = ObjectAnimator.ofFloat(mTwoBall, "radius", centerRadius, mMinRadius, centerRadius, mMaxRadius, centerRadius);
twoScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);
//第二個球移動動畫
ValueAnimator twoTranslationAnimtor = ValueAnimator.ofFloat(1, 0, -1, 0, 1);
twoTranslationAnimtor.setRepeatCount(ValueAnimator.INFINITE);
twoTranslationAnimtor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
float x = mCenterX + mDistance * value;
mTwoBall.centerX = x;
}
});
animatorSet = new AnimatorSet();
animatorSet.playTogether(oneScaleAnimator, oneTranslationAnimtor, twoScaleAnimator, twoTranslationAnimtor);
animatorSet.setDuration(mDuration);
animatorSet.setInterpolator(new DecelerateInterpolator());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mCenterX = getMeasuredWidth() / 2;
mCenterY = getMeasuredHeight() / 2;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w / 2;
mCenterY = h / 2;
}
@Override
protected void onDraw(Canvas canvas) {
//先畫半徑小的球,產生旋轉效果
if (mOneBall.radius > mTwoBall.radius) {
mPaint.setColor(mTwoBall.color);
canvas.drawCircle(mTwoBall.centerX, mCenterY, mTwoBall.radius, mPaint);
mPaint.setColor(mOneBall.color);
canvas.drawCircle(mOneBall.centerX, mCenterY, mOneBall.radius, mPaint);
} else {
mPaint.setColor(mOneBall.color);
canvas.drawCircle(mOneBall.centerX, mCenterY, mOneBall.radius, mPaint);
mPaint.setColor(mTwoBall.color);
canvas.drawCircle(mTwoBall.centerX, mCenterY, mTwoBall.radius, mPaint);
}
}
/**
* 監聽view的顯示隱藏
*
* @param changedView
* @param visibility
*/
@Override
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == GONE || visibility == INVISIBLE) {
stopAnimator();
} else {
startAnimator();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
startAnimator();
}
/**
* 開始動畫
*/
private void startAnimator() {
if (getVisibility() != VISIBLE || animatorSet == null || animatorSet.isRunning())
return;
animatorSet.start();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stopAnimator();
}
/**
* 停止動畫
*/
private void stopAnimator() {
if (animatorSet == null)
return;
animatorSet.end();
}
/**
* 球實體類
*/
public class Ball {
public float radius;//半徑
public float centerX;//圓心,x軸座標
public int color;//顏色
public float getRadius() {
return radius;
}
public void setRadius(float radius) {
this.radius = radius;
}
public float getCenterX() {
return centerX;
}
public void setCenterX(float centerX) {
this.centerX = centerX;
}
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
}
}
使用非常簡單:
<com.example.twoballrotationanim.widget.BallRotationAnim
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:distance="30dp"
app:duration="800"
app:one_color="#ff0000"
app:two_color="#0000ff" />