Some common operations of canvas are shown above





The above animation consists of three steps

  1. Six balls spinning animation
  2. Six balls diffuse and converge animation
  3. Water ripple animation


Specific implementation ideas, or the old custom, custom View

Public class SplashView extends View {private Paint mPaint; // Represents the center of the rotation circle // Screen center point privatefloat mCenterX;
    private floatmCenterY; Private int[] mCircleColors; private int[] mCircleColors; Private int mBackgroundColor = color.white; // The radius of the 6 balls is privatefloatmCircleRadius = 18; // Rotate the radius of the large circle privatefloatmRotateRadius = 90; // The radius of the current large circle privatefloatmCurrentRotateRadius = mRotateRadius; // The rotation Angle of the current great circle privatefloatmCurrentRotateAngle = 0F; Private ValueAnimator mValueAnimator; Private int mRotateDuration = 1200; private SplashState mState; // represents half of the diagonal length and the maximum radius of the diffusion circle privatefloatmDistance; // The radius of the diffusion circle is privatefloatmCurrentHoleRadius = 0F; // Private Paint mHolePaint; public SplashView(Context context) { super(context); init(context); } public SplashView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public SplashView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mPaint = new Paint(); mPaint.setAntiAlias(true); mHolePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mHolePaint.setStyle(Paint.Style.STROKE); mHolePaint.setColor(mBackgroundColor); MCircleColors = context.getResources().getintarRay (r.array.splash_circle_colors); }Copy the code
Overwrite onSizeChanged to get the size of the View, calculate mCenterX, | Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mCenterX = w * 1f / 2; mCenterY = h * 1f / 2; // The quadratic root of the sum of the squares of w and h is the Pythagorean theorem to solve the screen diagonal length divided by 2 indicates the maximum radius of the hollow circle of water ripples mDistance = (float) (Math.hypot(w, h) / 2);
}Copy the code
Private abstract class SplashState {abstract void drawState(Canvas Canvas); }Copy the code

Next, we will put the six small circles on the screen in turn, use trigonometric function, according to the specific number of angles, you can figure out the (x, y) coordinates of each ball, and then draw the ball





Private class RotateState extends SplashState {privateRotateState()
    {
        mValueAnimator = ValueAnimator.ofFloat(0, (float) (Math.PI * 2));
        mValueAnimator.setRepeatCount(2);
        mValueAnimator.setDuration(mRotateDuration);
        mValueAnimator.setInterpolator(new LinearInterpolator());
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                mCurrentRotateAngle = (float) animation.getAnimatedValue(); invalidate(); }}); / / to monitor animation end state Is executed after the end of the second animation mValueAnimator. AddListener (newAnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mState = new MerginState(); }}); mValueAnimator.start(); } @override void drawState(Canvas Canvas) {// drawBackground drawBackground(Canvas); // Draw a drawcircle (canvas); }}Copy the code
Private void drawCircle (Canvas Canvas) {float rotateAngle = (float) (Math.PI * 2 / mCircleColors.length);
    for(int i = 0; i < mCircleColors.length; i++) { // x = r * cos(a) + centX; // y = r * sin(a) + centY; //mCurrentRotateAngle recalculates the x and y coordinates of the ballfloat angle = i * rotateAngle + mCurrentRotateAngle;
        float cx = (float) (Math.cos(angle) * mCurrentRotateRadius+mCenterX);
        float cy = (float) (Math.sin(angle) * mCurrentRotateRadius+mCenterY);
        mPaint.setColor(mCircleColors[i]);
        canvas.drawCircle(cx, cy, mCircleRadius, mPaint);
    }
}

private void drawBackground(Canvas canvas)
{
    if(mCurrentHoleRadius > 0){// Draw hollow circlesfloat strokeWidth = mDistance - mCurrentHoleRadius;
        float radius = strokeWidth / 2 + mCurrentHoleRadius;
        mHolePaint.setStrokeWidth(strokeWidth);
        canvas.drawCircle(mCenterX,mCenterY, radius, mHolePaint);
    }else{
        canvas.drawColor(mBackgroundColor);
    }
}

@Override
protected void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    if (mState == null)
    {
        mState = new RotateState();
    }
    mState.drawState(canvas);
}Copy the code

When the first animation is complete, the diffusion animation of the ball is performed

Private class MerginState extends SplashState {privateMerginState()
    {
        mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);
        mValueAnimator.setDuration(mRotateDuration);
        mValueAnimator.setInterpolator(new OvershootInterpolator(10f));
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                mCurrentRotateRadius = (float) animation.getAnimatedValue(); invalidate(); }}); mValueAnimator.addListener(newAnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mState = new ExpandState(); }}); //mValueAnimator.start(); mValueAnimator.reverse(); } @override void drawState(Canvas Canvas) {// drawBackground drawBackground(Canvas); // Draw a drawcircle (canvas); }}Copy the code

When the second animation is complete, execute the water ripple animation to draw the hollow circle

Private class ExpandState extends SplashState{publicExpandState() {
            mValueAnimator = ValueAnimator.ofFloat(mCircleRadius, mDistance);
//            mValueAnimator.setRepeatCount(2);
            mValueAnimator.setDuration(mRotateDuration);
            mValueAnimator.setInterpolator(new LinearInterpolator());
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurrentHoleRadius = (float) animation.getAnimatedValue(); invalidate(); }}); mValueAnimator.start(); } @Override void drawState(Canvas canvas) { drawBackground(canvas); }}Copy the code
private void drawBackground(Canvas canvas)
{
    if(mCurrentHoleRadius > 0){// Draw hollow circlesfloat strokeWidth = mDistance - mCurrentHoleRadius;
        float radius = strokeWidth / 2 + mCurrentHoleRadius;
        mHolePaint.setStrokeWidth(strokeWidth);
        canvas.drawCircle(mCenterX,mCenterY, radius, mHolePaint);
    }else{ canvas.drawColor(mBackgroundColor); }}Copy the code

Finally, an XML file is attached

<? xml version="1.0" encoding="utf-8"? > <FrameLayout 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">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        android:src="@mipmap/content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.rx.myapplication.SplashView
        android:id="@+id/splash"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>Copy the code