小圆动态变化

效果图
本篇文章完全是根据HongChengDarren的文章仿写的,也是自己记录一下,以后看到类似的效果能知道怎么实现,轻喷!

一般做自定义view效果,首先是把效果放慢,然后分割,再一步一步的实现。首先动画效果分为三个部分:
第一:旋转动画,6个圆不停的旋转,如何让6个圆不停的旋转呢?
第二:聚合动画,6个圆向中间聚合。
第三:扩散动画,聚合完之后立马扩散。

首先解决第一个问题,想一想如何让6个圆不停的旋转,我们自定义view的时候如果想实现动画,要么通过改变一个变量然后结合invalidate方法来不停的绘制,要么通过动画来实现效果。如果用动画会有一些局限性,比如说需要用6张图片来执行这个动画,这样比较麻烦并且扩展性不好,所以只能从改变一个值然后invalidate来实现。
不管三七二十一,先绘制6个圆,这6个圆还有个起始位置,我日。起始位置怎么算?我们可以用360度除以6,每一份就是60度,然后用一个for循环在canvas.drawCircle()的时候算出小圆的x和y的位置,这样就好了。代码实现如下:

@Override void draw(Canvas canvas) {
			/***
			 * 这里需要绘制6个圆,然后就得计算每个圆的圆心点,每个圆的x,y的位置该怎么计算呢?
			 * 首先把一个圆整分为6份(因为总共是6个颜色,你也可以根据你的需求划分), 然后通过画图得知,第一个圆的x=大圆中心点x的坐标+(cosA * 半径)
			 * y=大圆中心点y+(sinA * 半径)
			 */
			double percent = 2 * Math.PI / 6;

			for (int i = 0; i < mCircleColors.length; i++) {
				// 当前的角度=初始角度+旋转角度(percent * i+mCurrentRotationValue)
				float cx = (float) (circleCX + (Math.cos(percent * i + mCurrentRotationValue) * mBigCircleRadius));
				float cy = (float) (circleCY + Math.sin(percent * i + mCurrentRotationValue) * mBigCircleRadius);

				mPaint.setColor(mCircleColors[i]);
				canvas.drawCircle(cx, cy, mSmallCircleRadius, mPaint);
			}
		}

有几个变量解释一下,mBigCircleRadius是大圆的半径,那个是大圆呢?小圆到中心点的距离,这个我们可以写死,写成屏幕宽度/4就行,mSmallCircleRadius这个是小圆半径,也是写死的。mCurrentRotationValue这个是旋转的角度,这个角度动态变化的情况下,就可以实现我们想要的效果。如何让这个角度动态变化呢?可以使用属性动画来不断的改变这个值,属性动画从0到2π之间变化。

第二个问题:聚合动画
其实看效果图会发现,6个圆是先向后再向前,刚开始我以为是通过设置三个属性动画值来达到这种效果,但是看作者的代码,才明白直接用AnticipateInterpolator这个插值器就好,666啊。下边我们分析如何让6个圆向中间聚合?其实我们是6个小圆围绕一个大圆在旋转,小圆到大圆的距离就是半径,我们可以改变这个半径的值,就可以实现我们的效果,代码如下:

private class MergeAnimator extends LoadingState {

		private ValueAnimator valueAnimator;

		public MergeAnimator() {
			init();
		}

		private void init() {
			valueAnimator = ValueAnimator.ofFloat(mBigCircleRadius, 0);
			valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

				@Override public void onAnimationUpdate(ValueAnimator animation) {
					mCurrentMergeRadius = (float) animation.getAnimatedValue();
					invalidate();
				}
			});
			valueAnimator.addListener(new AnimatorListenerAdapter() {

				@Override public void onAnimationEnd(Animator animation) {
					mLoadingState = new ExpendAnimator();
				}
			});
			// 开始的时候向后,然后向前甩
			valueAnimator.setInterpolator(new AnticipateInterpolator(3f));
			valueAnimator.setDuration(MERGE_DURATION);
			valueAnimator.start();
		}

		@Override void draw(Canvas canvas) {
			/***
			 * 这里需要绘制6个圆,然后就得计算每个圆的圆心点,每个圆的x,y的位置该怎么计算呢?
			 * 首先把一个圆整分为6份(因为总共是6个颜色,你也可以根据你的需求划分), 然后通过画图得知,第一个圆的x=大圆中心点x的坐标+(cosA * 半径)
			 * y=大圆中心点y+(sinA * 半径)
			 */
			double percent = 2 * Math.PI / mCircleColors.length;

			for (int i = 0; i < mCircleColors.length; i++) {
				// 当前的角度=初始角度+旋转角度(percent * i+mCurrentRotationValue)
				float cx = (float) (circleCX + (Math.cos(percent * i + mCurrentRotationValue) * mCurrentMergeRadius));
				float cy = (float) (circleCY + Math.sin(percent * i + mCurrentRotationValue) * mCurrentMergeRadius);

				mPaint.setColor(mCircleColors[i]);
				canvas.drawCircle(cx, cy, mSmallCircleRadius, mPaint);
			}
		}

	}

最后一个是扩散动画,我这里写的比较简单,仅仅只是绘制一个圆,这个圆的半径从0变化到整个屏幕的对角线的一半,为何是这个值?因为如果只是宽或高的一半,那么这个圆是填充不了整个屏幕。代码如下:

private class ExpendAnimator extends LoadingState {

		private ValueAnimator valueAnimator;

		public ExpendAnimator() {
			init();
		}

		private void init() {
			valueAnimator = ValueAnimator.ofFloat(0, mDiagonalRadius);
			valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

				@Override public void onAnimationUpdate(ValueAnimator animation) {
					mSpreadRadius = (float) animation.getAnimatedValue();
					invalidate();
				}
			});
			valueAnimator.setDuration(RATION_DURATION);
			valueAnimator.start();
		}

		@Override void draw(Canvas canvas) {
			mPaint.setColor(Color.WHITE);
			canvas.drawCircle(circleCX, circleCY, mSpreadRadius, mPaint);
		}

	}

全部代码如下:

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.LinearInterpolator;

import com.wxj.design.pattern.R;

import java.util.Map;

/**
 *
 * 1.旋转动画 6个圆旋转
 *
 * 2.聚合动画 6个圆向中间聚合
 *
 * 3.扩散动画 扩散到整个界面 先绘制一个圆,圆的半径是屏幕对角线的一半
 *
 * Created by wuxiaojun on 2018/11/20.
 */

public class YahuView extends View {

	private final int		RATION_DURATION			= 2000;

	private final int		MERGE_DURATION			= 1000;

	private int[]			mCircleColors;

	private int				mSmallCircleRadius;							// 小圆半径

	private int				mBigCircleRadius;							// 大圆半径

	private int				circleCX, circleCY;							// 大圆的中心点

	private Paint			mPaint;										// 画笔

	private LoadingState	mLoadingState;

	private float			mCurrentRotationValue	= 0f;				// 旋转动画的值

	private float			mCurrentMergeRadius		= mBigCircleRadius;	// 当前聚合动画的半径

	private float			mDiagonalRadius;							// 屏幕的对角线的一半

	private float			mSpreadRadius;								// 扩散圆的半径

	public YahuView(Context context) {
		this(context, null);
	}

	public YahuView(Context context, @Nullable AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public YahuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);

		mCircleColors = getContext().getResources().getIntArray(R.array.splash_circle_colors);

		mPaint = new Paint();
		mPaint.setStyle(Paint.Style.FILL);
	}

	@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

		mSmallCircleRadius = getMeasuredWidth() / 26;
		mBigCircleRadius = getMeasuredWidth() / 3;

		circleCX = getMeasuredWidth() / 2;
		circleCY = getMeasuredHeight() / 2;

		mDiagonalRadius = (float) Math.sqrt((circleCX * circleCY + circleCY * circleCY));
	}

	@Override protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		if (mLoadingState == null) {
			mLoadingState = new RotationAnimator();
		}

		mLoadingState.draw(canvas);
	}

	public void disapear() {
		// 关闭旋转动画
		if (mLoadingState instanceof RotationAnimator) {
			((RotationAnimator) mLoadingState).cancel();
		}
		// 开启聚合动画
		mLoadingState = new MergeAnimator();

	}

	private abstract class LoadingState {

		abstract void draw(Canvas canvas);
	}

	private class ExpendAnimator extends LoadingState {

		private ValueAnimator valueAnimator;

		public ExpendAnimator() {
			init();
		}

		private void init() {
			valueAnimator = ValueAnimator.ofFloat(0, mDiagonalRadius);
			valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

				@Override public void onAnimationUpdate(ValueAnimator animation) {
					mSpreadRadius = (float) animation.getAnimatedValue();
					invalidate();
				}
			});
			valueAnimator.setDuration(RATION_DURATION);
			valueAnimator.start();
		}

		@Override void draw(Canvas canvas) {
			mPaint.setColor(Color.WHITE);
			canvas.drawCircle(circleCX, circleCY, mSpreadRadius, mPaint);
		}

	}

	private class MergeAnimator extends LoadingState {

		private ValueAnimator valueAnimator;

		public MergeAnimator() {
			init();
		}

		private void init() {
			valueAnimator = ValueAnimator.ofFloat(mBigCircleRadius, 0);
			valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

				@Override public void onAnimationUpdate(ValueAnimator animation) {
					mCurrentMergeRadius = (float) animation.getAnimatedValue();
					invalidate();
				}
			});
			valueAnimator.addListener(new AnimatorListenerAdapter() {

				@Override public void onAnimationEnd(Animator animation) {
					mLoadingState = new ExpendAnimator();
				}
			});
			// 开始的时候向后,然后向前甩
			valueAnimator.setInterpolator(new AnticipateInterpolator(3f));
			valueAnimator.setDuration(MERGE_DURATION);
			valueAnimator.start();
		}

		@Override void draw(Canvas canvas) {
			/***
			 * 这里需要绘制6个圆,然后就得计算每个圆的圆心点,每个圆的x,y的位置该怎么计算呢?
			 * 首先把一个圆整分为6份(因为总共是6个颜色,你也可以根据你的需求划分), 然后通过画图得知,第一个圆的x=大圆中心点x的坐标+(cosA * 半径)
			 * y=大圆中心点y+(sinA * 半径)
			 */
			double percent = 2 * Math.PI / mCircleColors.length;

			for (int i = 0; i < mCircleColors.length; i++) {
				// 当前的角度=初始角度+旋转角度(percent * i+mCurrentRotationValue)
				float cx = (float) (circleCX + (Math.cos(percent * i + mCurrentRotationValue) * mCurrentMergeRadius));
				float cy = (float) (circleCY + Math.sin(percent * i + mCurrentRotationValue) * mCurrentMergeRadius);

				mPaint.setColor(mCircleColors[i]);
				canvas.drawCircle(cx, cy, mSmallCircleRadius, mPaint);
			}
		}

	}

	private class RotationAnimator extends LoadingState {

		private ValueAnimator mValueAnimator;

		public RotationAnimator() {
			init();
		}

		private void init() {
			mValueAnimator = ValueAnimator.ofFloat(0f, (float) (2f * Math.PI));
			mValueAnimator.setDuration(RATION_DURATION);
			mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

				@Override public void onAnimationUpdate(ValueAnimator animation) {
					mCurrentRotationValue = (float) animation.getAnimatedValue();
					invalidate();
				}
			});
			mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
			mValueAnimator.setInterpolator(new LinearInterpolator());
			mValueAnimator.start();
		}

		@Override void draw(Canvas canvas) {
			/***
			 * 这里需要绘制6个圆,然后就得计算每个圆的圆心点,每个圆的x,y的位置该怎么计算呢?
			 * 首先把一个圆整分为6份(因为总共是6个颜色,你也可以根据你的需求划分), 然后通过画图得知,第一个圆的x=大圆中心点x的坐标+(cosA * 半径)
			 * y=大圆中心点y+(sinA * 半径)
			 */
			double percent = 2 * Math.PI / mCircleColors.length;

			for (int i = 0; i < mCircleColors.length; i++) {
				// 当前的角度=初始角度+旋转角度(percent * i+mCurrentRotationValue)
				float cx = (float) (circleCX + (Math.cos(percent * i + mCurrentRotationValue) * mBigCircleRadius));
				float cy = (float) (circleCY + Math.sin(percent * i + mCurrentRotationValue) * mBigCircleRadius);

				mPaint.setColor(mCircleColors[i]);
				canvas.drawCircle(cx, cy, mSmallCircleRadius, mPaint);
			}
		}

		public void cancel() {
			mValueAnimator.cancel();
		}

	}

}

activity的代码

public class YahuActivity extends Activity {

	@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_yahu);

		final YahuView yahuView = (YahuView) findViewById(R.id.id_yahuview);

		new Handler() {

			@Override public void handleMessage(Message msg) {
				yahuView.disapear();
			}
		}.sendEmptyMessageDelayed(1, 5000);
	}

}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <com.wxj.design.pattern.view.YahuView
        android:id="@+id/id_yahuview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

颜色值

<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">#ff3891</color>

    <array name="splash_circle_colors">
        <item>@color/blue</item>
        <item>@color/pink</item>
        <item>@color/aqua</item>
        <item>@color/yellow</item>
        <item>@color/orange</item>
        <item>@color/green</item>
    </array>

再次声明,本篇文章仅仅只是自己记录。具体可看原创作者大佬的博客

猜你喜欢

转载自blog.csdn.net/u010648159/article/details/84350102