一、動畫
一般動畫我們分為三大類:幀動畫、補間動畫(Tween Animation)、屬性動畫。
幀動畫我們一般對gif做動畫;補間動畫又分為:Alpha、Scale、Translate、Rotate是直接作用到view上面的,所以有很多的局限性,很多復雜的動畫都不能實現(xiàn)。
在3.0后出現(xiàn)了屬性動畫,是直接對控件的屬性進行修改,所以可以完成很多復雜的動畫,下面我們可以看一下這個效果怎么實現(xiàn):
device-2018-07-12-154743.gif
二、實現(xiàn)動畫
1, 我們拿到這個設計圖,首先應該理清思路拆分動畫的效果步驟:
第一步:畫六個圓
第二步:逆時針或者順時針旋轉(zhuǎn)(旋轉(zhuǎn)狀態(tài))
第三步:六個圓聚合動畫(聚合狀態(tài))
第四步:從聚合后的一個圓擴散,顯示下層布局(擴散狀態(tài))
2,逐步實現(xiàn)上面的步驟
1)通過對上面的分析,我們知道這中間動畫的執(zhí)行有三種狀態(tài),所以我們可以采用設計模式中狀態(tài)模式,他們?nèi)齻€狀態(tài)都要對圖形繪制進行操作,所以我們創(chuàng)建一個抽象類,其他狀態(tài)類都繼承它:
//動畫有三種狀態(tài):旋轉(zhuǎn),聚合,擴散,這里采用設計模式的狀態(tài)模式
public abstract class SplashState {
abstract void drawState(Canvas canvas);
//取消動畫
void cancelAnimator() {
if (mAnimator != null) {
mAnimator.cancel();
}
}
}
2)通過分析:旋轉(zhuǎn),聚合,擴散動畫我們都可以使用屬性動畫,通過修改屬性值可以達到動畫效果,所以在抽象類中實現(xiàn)了動畫的結束方法;
采用設置模式狀態(tài)模式,這里我們有三種狀態(tài),就要三種狀態(tài)分別繼承SplashState 類,如下:
//旋轉(zhuǎn)狀態(tài)
public class RotationState extends SplashState {
@Override
void drawState(Canvas canvas) {
}
}
//聚合狀態(tài)
public class MergeState extends SplashState {
@Override
void drawState(Canvas canvas) {
}
}
//擴散狀態(tài)
public class SpreadState extends SplashState {
@Override
void drawState(Canvas canvas) {
}
}
3)第一次進來執(zhí)行rotation狀態(tài),所以在繪制onDraw里面初始化狀態(tài)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//第一次進來執(zhí)行旋轉(zhuǎn)動畫
if (mState == null) {
mState = new RotationState();
}
//進行繪制
mState.drawState(canvas);
}
4)實現(xiàn)rotation旋轉(zhuǎn)動畫
//旋轉(zhuǎn)狀態(tài)
public class RotationState extends SplashState {
public RotationState() {
//動畫執(zhí)行一次旋轉(zhuǎn)360°
mAnimator = ValueAnimator.ofFloat((float) (Math.PI * 2), 0f);//逆時針
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//獲取旋轉(zhuǎn)值
mRotationDegrees = (float) valueAnimator.getAnimatedValue();
//重繪
invalidate();
}
});
//設置線性插值器
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setDuration(mDuration);
//設置動畫執(zhí)行次數(shù)時間,INFINITE:無數(shù)次
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
drawBackGround(canvas);
drawCircles(canvas);
}
}
private void drawCircles(Canvas canvas) {
//算出平均角度
float averageDegree = (float) (2 * Math.PI / mCircleColors.length);
//遍歷繪制畫圓
for (int i = 0; i < mCircleColors.length; i++) {
//每個圓相對于屏幕中心點的角度
float circleDegree = averageDegree * i;
//旋轉(zhuǎn)后的實際角度
float actualCurrentDress = circleDegree + mRotationDegrees;
float cx = (float) (mChangeRadius * Math.cos(actualCurrentDress) + mCenterX);
float cy = (float) (mChangeRadius * Math.sin(actualCurrentDress) + mCenterY);
mCirclePaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx, cy, mSingleCircleRadius, mCirclePaint);
}
}
private void drawBackGround(Canvas canvas) {
//畫布背景
canvas.drawColor(mBackGroundColor);
}
5)實現(xiàn)聚合動畫
//聚合狀態(tài)
public class MergeState extends SplashState {
public MergeState() {
mAnimator = ValueAnimator.ofFloat(mCircleRadius, 0);
mAnimator.setDuration(mDuration);
mAnimator.setInterpolator(new BounceInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mChangeRadius = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mState != null && mState instanceof MergeState) {
//結束聚合動畫后再執(zhí)行擴散動畫
mState = new SpreadState();
}
}
});
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
drawBackGround(canvas);
drawCircles(canvas);
}
}
6)實現(xiàn)擴散動畫
//擴散狀態(tài)
public class SpreadState extends SplashState {
public SpreadState() {
mAnimator = ValueAnimator.ofFloat(mSingleCircleRadius, mDiagonal);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mHollowRadius = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mAnimator.setDuration(mDuration);
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
float paintWidth = mDiagonal - mHollowRadius;//畫筆寬度
mHollowPaint.setStrokeWidth(paintWidth);
canvas.drawCircle(mCenterX, mCenterY, mHollowRadius + paintWidth / 2, mHollowPaint);
}
}
這樣我們動畫的三個狀態(tài)就已經(jīng)完成
三、完整代碼
自定義view代碼如下:
public class SplashView extends View {
private ValueAnimator mAnimator;
private SplashState mState;
private int[] mCircleColors;
private Paint mCirclePaint;
//單個圓的半徑
private float mSingleCircleRadius = 18f;
//六個圓圍成的圓的半徑
private float mCircleRadius = 90;
//聚合的時候圓的初始半徑
private float mChangeRadius = mCircleRadius;
private float mRotationDegrees = 0;
private int mDuration = 1200;//單位毫秒
private float mCenterX, mCenterY;//算出控件的中心位置
private float mDiagonal;//對角線
private int mBackGroundColor = Color.WHITE;//背景顏色
private Paint mHollowPaint;//空心圓的畫筆
private float mHollowRadius;//空心圓的半徑
public SplashView(Context context) {
super(context);
}
public SplashView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SplashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void init() {
//找到所要畫的圓的顏色,colors中定義的顏色數(shù)組
mCircleColors = getContext().getResources().getIntArray(R.array.splash_circle_colors);
//初始化畫圓的畫筆
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setAntiAlias(true);
//設置填充
mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mHollowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHollowPaint.setAntiAlias(true);
mHollowPaint.setStyle(Paint.Style.STROKE); //設置不填充畫筆
mHollowPaint.setColor(mBackGroundColor);//設置畫筆顏色
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w / 2;
mCenterY = h / 2;
mDiagonal = (float) (Math.sqrt((w * w + h * h)) / 2);
init();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//第一次進來執(zhí)行旋轉(zhuǎn)動畫
if (mState == null) {
mState = new RotationState();
}
mState.drawState(canvas);
}
//還原狀態(tài)
public void setInitialState() {
if (mState != null) {
mState.cancelAnimator();
}
//還原
mChangeRadius = mCircleRadius;
mState = new RotationState();
invalidate();
}
public void changeRotationState() {
//從旋轉(zhuǎn)狀態(tài)變成聚合狀態(tài)
if (mState != null && mState instanceof RotationState) {
//取消旋轉(zhuǎn)動畫
RotationState rotationState = (RotationState) mState;
rotationState.cancelAnimator();
post(new Runnable() {
@Override
public void run() {
mState = new MergeState();
}
});
}
}
//動畫有三種狀態(tài):旋轉(zhuǎn),聚合,擴散,這里采用設計模式的狀態(tài)模式
public abstract class SplashState {
abstract void drawState(Canvas canvas);
//取消動畫
void cancelAnimator() {
if (mAnimator != null) {
mAnimator.cancel();
}
}
}
//旋轉(zhuǎn)狀態(tài)
public class RotationState extends SplashState {
public RotationState() {
//動畫執(zhí)行一次旋轉(zhuǎn)360°
mAnimator = ValueAnimator.ofFloat((float) (Math.PI * 2), 0f);//逆時針
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//獲取旋轉(zhuǎn)值
mRotationDegrees = (float) valueAnimator.getAnimatedValue();
//重繪
invalidate();
}
});
//設置線性插值器
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.setDuration(mDuration);
//設置動畫執(zhí)行次數(shù)時間,INFINITE:無數(shù)次
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
drawBackGround(canvas);
drawCircles(canvas);
}
}
private void drawCircles(Canvas canvas) {
//算出平均角度
float averageDegree = (float) (2 * Math.PI / mCircleColors.length);
//遍歷繪制畫圓
for (int i = 0; i < mCircleColors.length; i++) {
//每個圓相對于屏幕中心點的角度
float circleDegree = averageDegree * i;
//旋轉(zhuǎn)后的實際角度
float actualCurrentDress = circleDegree + mRotationDegrees;
float cx = (float) (mChangeRadius * Math.cos(actualCurrentDress) + mCenterX);
float cy = (float) (mChangeRadius * Math.sin(actualCurrentDress) + mCenterY);
mCirclePaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx, cy, mSingleCircleRadius, mCirclePaint);
}
}
private void drawBackGround(Canvas canvas) {
//畫布背景
canvas.drawColor(mBackGroundColor);
}
//聚合狀態(tài)
public class MergeState extends SplashState {
public MergeState() {
mAnimator = ValueAnimator.ofFloat(mCircleRadius, 0);
mAnimator.setDuration(mDuration);
mAnimator.setInterpolator(new BounceInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mChangeRadius = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mState != null && mState instanceof MergeState) {
mState = new SpreadState();
}
}
});
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
drawBackGround(canvas);
drawCircles(canvas);
}
}
//擴散狀態(tài)
public class SpreadState extends SplashState {
public SpreadState() {
mAnimator = ValueAnimator.ofFloat(mSingleCircleRadius, mDiagonal);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mHollowRadius = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mAnimator.setDuration(mDuration);
mAnimator.start();
}
@Override
void drawState(Canvas canvas) {
float paintWidth = mDiagonal - mHollowRadius;//畫筆寬度
mHollowPaint.setStrokeWidth(paintWidth);
canvas.drawCircle(mCenterX, mCenterY, mHollowRadius + paintWidth / 2, mHollowPaint);
}
}
}
MainActivity代碼:
public class MainActivity extends AppCompatActivity {
private SplashView mSplashView;
Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSplashView = findViewById(R.id.splashView);
run();
}
public void onClick(View view) {
mSplashView.setInitialState();
run();
}
public void run() {
//模擬請求網(wǎng)絡
handler.postDelayed(new Runnable() {
@Override
public void run() {
mSplashView.changeRotationState();
}
}, 4000);
}
}
xml代碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/pic" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="還原狀態(tài)" />
<com.example.administrator.attranimatorapp.SplashView
android:id="@+id/splashView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
這樣我們就實現(xiàn)了上面的效果