Go directly to the picture, first to see the effect:

If you want to know how it works, look down; Let’s start with the XML layout:

<? The 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:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="sz.com.transitionpagedemo.MainActivity"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:layout_width="match_parent"  android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/men110"/> <! - custom animation VIEW - > < sz.com.transitionpagedemo.SplashView android: id = "@ + id/splashView" android: layout_width = "match_parent" android:layout_height="match_parent" /> </FrameLayout> </RelativeLayout>Copy the code

From the layout, it’s pretty simple, FrameLayout, ImageView first, that’s what we see at the end of the animation; And then we add our own SplashView animation; Okay, so let’s focus on custom SplashViews;

package sz.com.transitionpagedemo; 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.util.AttributeSet; import android.view.View; import android.view.animation.LinearInterpolator; import android.view.animation.OvershootInterpolator; public class SplashView extends View { private int mBigCircleRadius=120; Private int mSmallCircleRadius=18; Private int[] mColorCombination={getResources().getColor(r.color.colorprimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent), getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent)}; Private int mWidth; Private int mHight; // private Paint mPaint; private PanDraw mPanDraw; private float mRotationAngle; Private ValueAnimator ValueAnimator; private int mDiffusionRadius; // Diffusion radius private int mDiagonal; Public SplashView(Context Context, AttributeSet attrs) {super(Context, attrs); init(); } public SplashView(Context context) { super(context); init(); } private void init() {mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL_AND_STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHight = h; MDiagonal = (int) (math.sqrt (w*w+h*h)/2); } /** * Public void loadingCompleted(){valueanimator.cancel (); MPanDraw =new ShrinkDraw(); invalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mPanDraw==null){ mPanDraw = new SmallCircleDraw(); } mPanDraw.startDraw(canvas); Public abstract class PanDraw{// Start drawing public abstract void startDraw(Canvas Canvas); } private class SmallCircleDraw extends PanDraw{public SmallCircleDraw() {valueAnimator = private class SmallCircleDraw extends PanDraw{public SmallCircleDraw() {valueAnimator = ValueAnimator.ofFloat(0f,360F); valueAnimator.setDuration(2000); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mRotationAngle= (float) animation.getAnimatedValue(); invalidate(); }}); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); Valueanimator.start (); } @Override public void startDraw(Canvas canvas) { drawBackground(canvas); drawSmallCircle(canvas); }} private void drawSmallCircle(Canvas Canvas){if (mColorCombination==null){return; } canvas.translate(mWidth/2,mHight/2); for (int i=0; i<mColorCombination.length; i++){ canvas.save(); / / save the canvas canvas. Rotate (360 * I/mColorCombination. Length + mRotationAngle, 0, 0). // Rotate the canvas mPaint. SetColor (mColorCombination[I]); / / set the length of the canvas canvas. Methods like drawCircle (mBigCircleRadius, 0, mSmallCircleRadius, mPaint); / / draw circles canvas. Restore (); Private void drawBackground(canvas) {canvas. DrawColor (color.white); */ Private ShrinkDraw extends PanDraw{public ShrinkDraw() {final ValueAnimator valueAnimator1=ValueAnimator.ofInt(0,mBigCircleRadius); valueAnimator1.setDuration(1000); valueAnimator1.setInterpolator(new OvershootInterpolator(20)); / / here to do a play effect valueAnimator1 addUpdateListener (new ValueAnimator. AnimatorUpdateListener () {@ Override public void onAnimationUpdate(ValueAnimator animation) { mBigCircleRadius= (int) animation.getAnimatedValue(); invalidate(); }}); valueAnimator1.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mPanDraw=new DiffusionDraw(); // Start drawing invalidate() with diffuse water ripples; }}); valueAnimator1.reverse(); } @override public void startDraw(Canvas) {drawBackground(Canvas); drawSmallCircle(canvas); }} private class DiffusionDraw extends PanDraw{public DiffusionDraw() {final ValueAnimator valueAnimator1=ValueAnimator.ofInt(0,mDiagonal); valueAnimator1.setDuration(1000); valueAnimator1.setInterpolator(new LinearInterpolator()); valueAnimator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDiffusionRadius= (int) animation.getAnimatedValue(); invalidate(); }}); valueAnimator1.start(); } /** * @param canvas * // To draw a diffuse water ripple effect, I am constantly changing the width of the brush. The radius of the circle is the radius of the circle plus half of the brush width. mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.STROKE); canvas.translate(mWidth/2,mHight/2); float mStrokeWidth=(mDiagonal-mDiffusionRadius); mPaint.setStrokeWidth(mStrokeWidth); Canvas. Methods like drawCircle (0, 0, + mStrokeWidth mDiffusionRadius / 2, mPaint); }}Copy the code

}

  • A. At first we do some initialization work, such as setting the brush, then we get the width and height of the View in onSizeChanged method, and calculate the length of half of the View diagonal;

  • B. Start to draw the animation, which is divided into three parts. The first part is to draw a small circle, with six small circles rotating around the center point; The second is the small circle spreading out and then contracting into a circle, and the third is the effect of diffusing water ripples; All three animations are to be drawn, so I’m using strategy mode here; Let’s start with an abstract class called PanDraw;

    Public abstract class PanDraw{// Start drawing public abstract void startDraw(Canvas Canvas); }Copy the code
  • C. Implement startDraw(Canvas Canvas) method and start drawing;

  • This animation is set as infinite multiple animation. During the drawing process, first move the center point of the canvas to the center point of the View, and then calculate the total number of small circles to draw a small circle. Here, the rotation of the canvas is used to draw a small circle;

    Canvas. Rotate (360 * I/mColorCombination. Length + mRotationAngle, 0, 0). // Rotate the canvasCopy the code
  • E. Draw the diffused small circle. The drawing process is the same as above, but the position of the small circle from the center point is constantly changed, and then the small circle is merged together. Check out my article on Android advanced UI tween animation and property animation.

    valueAnimator1.setInterpolator(new OvershootInterpolator(20)); // Create a pop effect hereCopy the code
  • F. Draw diffuse water ripple effect, here skillfully use the brush width Settings; Keep changing the width of the brush. The width of the brush is half the diagonal length of the screen minus the radius of the middle spread circle, which is the radius of the middle spread circle plus half the width of the brush;

    @Override public void startDraw(Canvas canvas) { mPaint.setColor(Color.WHITE); mPaint.setStyle(Paint.Style.STROKE); canvas.translate(mWidth/2,mHight/2); float mStrokeWidth=(mDiagonal-mDiffusionRadius); mPaint.setStrokeWidth(mStrokeWidth); Canvas. Methods like drawCircle (0, 0, + mStrokeWidth mDiffusionRadius / 2, mPaint);Copy the code

    }

  • G. After loading, the rotation state of the small circle changes to the contraction state

    /** * Public void loadingCompleted(){valueanimator.cancel (); MPanDraw =new ShrinkDraw(); invalidate(); }Copy the code
  • H. In MainActivity, set the rotation time of the small circle to transition to the small circle diffusion effect;

    public class MainActivity extends AppCompatActivity { private SplashView splashView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); splashView = (SplashView) findViewById(R.id.splashView); Handler handler =new Handler(); Handler.postdelayed (new Runnable() {@override public void run() {//5 seconds later the delayed effect will be small; splashView.loadingCompleted(); }}, 5000); }}Copy the code

I do Android development for many years, later will be updated on android advanced UI,NDK development, performance optimization and other articles, more please pay attention to my wechat public number: thank you!