先看運行效果
第一個動畫:繪製六個點旋轉動畫
- 繪製六個小圓
- mCircleColors這裏一共是6中顏色,分別對應6個小圓顏色
- 每個圓得角度= 2π/小圓的個數
- 每個圓得x座標 = 圓半徑*cos(a) +圓心X座標
- 每個圓得y座標 = 圓半徑*sin(a) +圓心Y座標
- 每個小圓i*間隔角度 + 旋轉的角度 = 當前小圓的真是角度
六個點得繪製代碼
/**
* 繪製圓
* @param canvas
*/
private void drawCircles(Canvas canvas) {
//每個小圓得角度 2π/小圓的個數
double rotationAngle = 2 * Math.PI / mCircleColors.length;
for (int i = 0; i < mCircleColors.length; i++) {
/**
* x = r*cos(a) +centerX
* y= r*sin(a) + centerY
* 每個小圓i*間隔角度 + 旋轉的角度 = 當前小圓的真是角度
*/
double angle = i*rotationAngle + mCurrentRotationAngle;
float cx = (float) (mCurrentRotationRadius*Math.cos(angle) + mCenterX);
float cy = (float) (mCurrentRotationRadius*Math.sin(angle) + mCenterY);
mPaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
- 六個點得旋轉
- 動畫得改變範圍0~2π
- 設置動畫LinearInterpolator爲線性變化
- 設置動畫狀態爲ValueAnimator.INFINITE 一直旋轉
- 通過動態改變偏移角度mCurrentRotationAngle的值,來達到旋轉效果
public RotateState(){
mAnimator = ValueAnimator.ofFloat(0f,(float)Math.PI*2);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCurrentRotationAngle = (float) valueAnimator.getAnimatedValue();
Log.d(TAG,"--mCurrentRotationAngle--"+mCurrentRotationAngle);
postInvalidate();
}
});
mAnimator.setDuration(mRotationDuration);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
第二個動畫:縮放動畫
- 動態改變這個半徑R的值,來達到縮放功能
- 動畫的取值範圍是R到0
- 通過動態修改六個圓的繪製半徑mCurrentRotationRadius 達到縮放效果
public MergingState() {
//花1200ms,計算某個時刻當前的大圓半徑是多少? r~0中的某個值
mAnimator = ValueAnimator.ofFloat(mRotationRadius,0 );
mAnimator.setDuration(mRotationDuration);
mAnimator.setInterpolator(new OvershootInterpolator(10f));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 某個時刻當前的大圓半徑是多少?
mCurrentRotationRadius = (float)valueAnimator.getAnimatedValue();
Log.d(TAG,"--mCurrentRotationRadius--"+mCurrentRotationRadius);
invalidate();
}
});
mAnimator.start();
}
第三個動畫:水波紋擴散動畫
- 圖中R2=屏幕對角線長度/2
- R1就是空心圓半徑
- 畫筆的寬度strokeWidth(圖中紅色部分) = R2 - R1
- R2是固定大小,我們只需要動態修改R1的大小,就可以動態修改圖中紅色的面積,我們的demo是設置了白色
//得到畫筆的寬度 = 對角線/2 - 空心圓的半徑
float strokeWidth = mDiagonalDist - mHoleRadius;
mPaintBackground.setStrokeWidth(strokeWidth);
//畫圓的半徑 = 空心圓的半徑 + 畫筆的寬度/2
float radius = mHoleRadius + strokeWidth/2;
canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
下面是完整的代碼
public class MySplashView extends View {
private static final String TAG = "MySplashView";
// 屏幕正中心點座標
private float mCenterX;
private float mCenterY;
//屏幕對角線一半
private float mDiagonalDist;
// 小圓圈的顏色列表,在initialize方法裏面初始化
private int[] mCircleColors;
// 繪製圓的畫筆
private Paint mPaint = new Paint();
// 繪製背景的畫筆
private Paint mPaintBackground = new Paint();
// 整體的背景顏色
private int mSplashBgColor = Color.WHITE;
private ValueAnimator mAnimator;
private SplashState mState = null;
// 大圓(裏面包含很多小圓的)的半徑
private float mRotationRadius = 90;
//當前大圓旋轉角度(弧度)
private float mCurrentRotationAngle = 0F;
//當前大圓的半徑
private float mCurrentRotationRadius = mRotationRadius;
// 每一個小圓的半徑
private float mCircleRadius = 18;
// 大圓和小圓旋轉的時間
private long mRotationDuration = 1200; //ms
//空心圓初始半徑
private float mHoleRadius = 0F;
public MySplashView(Context context) {
super(context);
init(context);
}
public MySplashView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MySplashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCenterX = w / 2;
mCenterY = h / 2;
mDiagonalDist = (float) ((Math.sqrt(w*w+h*h))/2);
}
private void init(Context context) {
mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
//畫筆初始化
//消除鋸齒
mPaint.setAntiAlias(true);
mPaintBackground.setAntiAlias(true);
//設置樣式---邊框樣式--描邊
mPaintBackground.setStyle(Paint.Style.STROKE);
mPaintBackground.setColor(mSplashBgColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mState==null){
//開啓第一個旋轉動畫
mState = new RotateState();
}
mState.drawState(canvas);
}
/**
* 繪製旋轉小圓
* 旋轉動畫
*/
private class RotateState extends SplashState{
public RotateState(){
mAnimator = ValueAnimator.ofFloat(0f,(float)Math.PI*2);
mAnimator.setInterpolator(new LinearInterpolator());
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCurrentRotationAngle = (float) valueAnimator.getAnimatedValue();
Log.d(TAG,"--mCurrentRotationAngle--"+mCurrentRotationAngle);
postInvalidate();
}
});
mAnimator.setDuration(mRotationDuration);
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
mAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
//1.背景--擦黑板,塗成白色
drawBackground(canvas);
//2.繪製小圓
drawCircles(canvas);
}
}
/**
* 2.聚合動畫
* 要素:大圓的半徑不斷地變大--變小----》小圓的座標
*/
private class MergingState extends SplashState{
public MergingState() {
//花1200ms,計算某個時刻當前的大圓半徑是多少? r~0中的某個值
mAnimator = ValueAnimator.ofFloat(mRotationRadius,0 );
mAnimator.setDuration(mRotationDuration);
mAnimator.setInterpolator(new OvershootInterpolator(10f));
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// 某個時刻當前的大圓半徑是多少?
mCurrentRotationRadius = (float)valueAnimator.getAnimatedValue();
Log.d(TAG,"--mCurrentRotationRadius--"+mCurrentRotationRadius);
invalidate();
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mState = new ExpandState();
}
});
mAnimator.start();
// mAnimator.reverse();
}
@Override
public void drawState(Canvas canvas) {
//1.背景--擦黑板,塗成白色
drawBackground(canvas);
//2.繪製小圓
drawCircles(canvas);
}
}
/**
* 3.水波紋擴散動畫
* 畫一個空心圓----畫一個圓,讓它的畫筆的粗細變成很大---不斷地減小畫筆的粗細。
* 空心圓變化的範圍:0~ 對角線/2
*/
private class ExpandState extends SplashState{
public ExpandState() {
//花1200ms,計算某個時刻當前的空心圓的半徑是多少? r~0中的某個值
mAnimator = ValueAnimator.ofFloat(mCircleRadius, mDiagonalDist);
mAnimator.setDuration(mRotationDuration);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
//當前的空心圓的半徑是多少?
mHoleRadius = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
mAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
drawBackground(canvas);
}
}
private void drawBackground(Canvas canvas) {
if(mHoleRadius>0f){
//得到畫筆的寬度 = 對角線/2 - 空心圓的半徑
float strokeWidth = mDiagonalDist - mHoleRadius;
mPaintBackground.setStrokeWidth(strokeWidth);
//畫圓的半徑 = 空心圓的半徑 + 畫筆的寬度/2
float radius = mHoleRadius + strokeWidth/2;
canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
}else {
canvas.drawColor(mSplashBgColor);
}
}
/**
* 繪製圓
* @param canvas
*/
private void drawCircles(Canvas canvas) {
//每個小圓得角度 2π/小圓的個數
double rotationAngle = 2 * Math.PI / mCircleColors.length;
for (int i = 0; i < mCircleColors.length; i++) {
/**
* x = r*cos(a) +centerX
* y= r*sin(a) + centerY
* 每個小圓i*間隔角度 + 旋轉的角度 = 當前小圓的真是角度
*/
double angle = i*rotationAngle + mCurrentRotationAngle;
float cx = (float) (mCurrentRotationRadius*Math.cos(angle) + mCenterX);
float cy = (float) (mCurrentRotationRadius*Math.sin(angle) + mCenterY);
mPaint.setColor(mCircleColors[i]);
canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
}
}
public void splashDisappear(){
//開啓後面兩個動畫
//換模板--換狀態
if(mState!=null && mState instanceof RotateState){
//結束旋轉動畫
RotateState rotateState = (RotateState) mState;
rotateState.cancel();
post(new Runnable() {
@Override
public void run() {
mState = new MergingState();
}
});
}
}
//策略模式
private abstract class SplashState{
public abstract void drawState(Canvas canvas);
public void cancel(){
mAnimator.cancel();
}
}
}
mCircleColors顏色參數
<color name="splash_bg">#F8F6EC</color>
<color name="orange">#FF9600</color>
<color name="aqua">#02D1AC</color>
<color name="yellow">#FFD200</color>
<color name="blue">#00C6FF</color>
<color name="green">#00E099</color>
<color name="pink">#FF3892</color>
<array name="splash_circle_colors">
<item>@color/blue</item>
<item>@color/green</item>
<item>@color/pink</item>
<item>@color/orange</item>
<item>@color/aqua</item>
<item>@color/yellow</item>
</array>
mainactivity代碼
public class MainActivity extends AppCompatActivity {
private MySplashView splashView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
splashView = findViewById(R.id.splashview);
startLoadData();
}
Handler handler = new Handler();
private void startLoadData() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
//數據加載完畢,進入主界面--->開啓後面的兩個動畫
splashView.splashDisappear();
}
},5000);//延遲時間
}
}
R.layout.activity_main佈局
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/test"
tools:context=".MainActivity">
<com.zkq.myanim.MySplashView
android:id="@+id/splashview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
完整demo下載地址: demo下載地址