com.lovejjfg.arsenal.ui.widget.JumpBall.java Source code

Java tutorial

Introduction

Here is the source code for com.lovejjfg.arsenal.ui.widget.JumpBall.java

Source

/*
 *  Copyright (c) 2017.  Joe
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package com.lovejjfg.arsenal.ui.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.lovejjfg.arsenal.R;

/**
 * Created by Joe on 2017/2/21.
 * Email lovejjfg@gmail.com
 */

public class JumpBall extends View {
    private CirclePoint p0;
    private CirclePoint p1;
    private CirclePoint p2;
    private CirclePoint p3;
    private CirclePoint p4;
    private CirclePoint p5;
    private CirclePoint p6;
    private CirclePoint p7;
    private CirclePoint p8;
    private CirclePoint p9;
    private CirclePoint p10;
    private CirclePoint p11;

    /**
     * ?Path
     */
    private Path mPath;
    private Paint mPaint;

    /**
     * ?
     */
    private float mCircleRadius = 90;
    private int mWidth;
    private int mHeight;
    private float mTranslateValue = 0;
    private int mChange;
    private boolean isEnd;
    private int dropHeight = 300;
    private int pullRange = (int) (dropHeight * 0.15);
    private ValueAnimator pullAnimator;
    private ValueAnimator dropAnimator;
    private ValueAnimator radioAnimator;
    private float mCurrentRadio;
    private int dropTime;

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

    public JumpBall(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    @SuppressWarnings("unused")
    public void setBallRadius(float mCircleRadius) {
        this.mCircleRadius = mCircleRadius;
        initData();
    }

    public JumpBall(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.JumpBall, defStyleAttr, 0);
        mCircleRadius = a.getDimension(R.styleable.JumpBall_ballRadius,
                getResources().getDisplayMetrics().density * 20);
        initData();
        a.recycle();
        init();
    }

    private void initData() {
        dropHeight = (int) (6 * mCircleRadius);
        pullRange = (int) (mCircleRadius * 0.5f);
        dropTime = (int) (Math.sqrt(dropHeight * 800));
    }

    private void resetPoints() {
        //Care about dropHeight bigger than mHeight
        float m = mCircleRadius * CIRCLE_VALUE;
        int centerY = (int) ((mHeight - dropHeight) / 2 + mCircleRadius);
        int topY = (mHeight - dropHeight) / 2;
        int BottomY = (int) ((mHeight - dropHeight) / 2 + 2 * mCircleRadius);

        p2.setPoint(mWidth / 2 + mCircleRadius, centerY + m);
        p3.setPoint(mWidth / 2 + mCircleRadius, centerY);
        p4.setPoint(mWidth / 2 + mCircleRadius, centerY - m);

        p5.setPoint(mWidth / 2 + m, topY);
        p6.setPoint(mWidth / 2, topY);
        p7.setPoint(mWidth / 2 - m, topY);

        p8.setPoint(mWidth / 2 - mCircleRadius, centerY - m);
        p9.setPoint(mWidth / 2 - mCircleRadius, centerY);
        p10.setPoint(mWidth / 2 - mCircleRadius, centerY + m);

        p11.setPoint(mWidth / 2 - m, BottomY);
        p0.setPoint(mWidth / 2, BottomY);
        p1.setPoint(mWidth / 2 + m, BottomY);
    }

    /**
     * ?
     */
    private static final float CIRCLE_VALUE = 0.551915024494f;

    private void initPoints() {
        //Care about dropHeight bigger than mHeight
        float m = mCircleRadius * CIRCLE_VALUE;
        int centerY = (int) ((mHeight - dropHeight) / 2 + mCircleRadius);
        int topY = (mHeight - dropHeight) / 2;
        int bottomY = (int) ((mHeight - dropHeight) / 2 + 2 * mCircleRadius);

        p2 = new CirclePoint(mWidth / 2 + mCircleRadius, centerY + m);//2m
        p3 = new CirclePoint(mWidth / 2 + mCircleRadius, centerY);//m
        p4 = new CirclePoint(mWidth / 2 + mCircleRadius, centerY - m);//0

        p5 = new CirclePoint(mWidth / 2 + m, topY);
        p6 = new CirclePoint(mWidth / 2, topY);
        p7 = new CirclePoint(mWidth / 2 - m, topY);

        p8 = new CirclePoint(mWidth / 2 - mCircleRadius, centerY - m);
        p9 = new CirclePoint(mWidth / 2 - mCircleRadius, centerY);
        p10 = new CirclePoint(mWidth / 2 - mCircleRadius, centerY + m);

        p11 = new CirclePoint(mWidth / 2 - m, bottomY);
        p0 = new CirclePoint(mWidth / 2, bottomY);
        p1 = new CirclePoint(mWidth / 2 + m, bottomY);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = w;
        mHeight = h;
        initPoints();
        if (radioAnimator == null) {
            radioAnimator = ValueAnimator.ofFloat(mCircleRadius, mWidth * 2);
        } else {
            radioAnimator.setFloatValues(mCircleRadius, mWidth * 2);
        }
        super.onSizeChanged(w, h, oldw, oldh);
    }

    private void init() {
        if (mPaint == null) {
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
            mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        }

        if (mPath == null) {
            mPath = new Path();
        }

        pullAnimator = ValueAnimator.ofInt(0, pullRange, 0);
        pullAnimator.setDuration((long) (dropTime * 0.5f));
        pullAnimator.addUpdateListener(animation -> {
            mChange = (Integer) animation.getAnimatedValue();
            if (animation.getAnimatedFraction() > 0.3) {
                dropAnimator.setIntValues(dropHeight, 0, dropHeight);
                dropAnimator.setDuration(dropTime * 2);
                dropAnimator.start();
            }
            Log.e("TAG", "onAnimationUpdate: " + mChange);
            invalidate();
        });

        pullAnimator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {

            }
        });

        dropAnimator = ValueAnimator.ofInt(0, dropHeight);
        //        dropAnimator.setRepeatMode(ValueAnimator.REVERSE);
        //        dropAnimator.setRepeatCount(20);
        dropAnimator.setDuration(dropTime);
        dropAnimator.setInterpolator(new LinearInterpolator());
        dropAnimator.addUpdateListener(animation -> {
            mTranslateValue = (Integer) animation.getAnimatedValue();
            invalidate();
        });

        dropAnimator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                //                dropAnimator = ValueAnimator.ofInt(dropHeight, 0, dropHeight);
                pullAnimator.start();
            }
        });
        dropAnimator.start();

        radioAnimator = ValueAnimator.ofFloat(mCircleRadius, mWidth);
        //        radioAnimator.setRepeatMode(ValueAnimator.REVERSE);
        //        radioAnimator.setRepeatCount(20);
        radioAnimator.setDuration(600);
        radioAnimator.setInterpolator(new LinearInterpolator());
        radioAnimator.addUpdateListener(animation -> {
            mCurrentRadio = (float) animation.getAnimatedValue();
            invalidate();
        });

        radioAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                pullAnimator.cancel();
                dropAnimator.cancel();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                isEnd = false;
                mCurrentRadio = mCircleRadius;
                if (dismissListener != null) {
                    dismissListener.onDismiss();
                }
                setVisibility(GONE);

            }
        });
        //        radioAnimator.start();
    }

    public onDismissListener getDismissListener() {
        return dismissListener;
    }

    public void setDismissListener(onDismissListener dismissListener) {
        this.dismissListener = dismissListener;
    }

    private onDismissListener dismissListener;

    public interface onDismissListener {
        void onDismiss();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!isEnd) {
            drawCirclePath(canvas);
        } else {
            canvas.drawCircle(mWidth / 2, mHeight / 2 + mTranslateValue, mCurrentRadio, mPaint);
        }
    }

    /**
     * ?
     * ? 8 9 10  - dx  4 3 2 ? + dx  5 6 7 + dy  11 0 1 
     */
    private void drawCirclePath(Canvas canvas) {
        resetPoints();
        p0.y += mTranslateValue;
        p6.y += mTranslateValue;
        p1.y += mTranslateValue;
        p2.y += mTranslateValue + mChange * 0.5;
        p3.y += mTranslateValue + mChange * 0.5;
        p4.y += mTranslateValue + mChange * 0.5;
        p5.y += mTranslateValue;
        p7.y += mTranslateValue;
        p8.y += mTranslateValue + mChange * 0.5;
        p9.y += mTranslateValue + mChange * 0.5;
        p10.y += mTranslateValue + mChange * 0.5;
        p11.y += mTranslateValue;

        p8.x -= mChange;
        p9.x -= mChange;
        p10.x -= mChange;

        p4.x += mChange;
        p3.x += mChange;
        p2.x += mChange;

        p5.y += mChange;
        p6.y += mChange;
        p7.y += mChange;

        mPath.reset();
        mPath.moveTo(p0.x, p0.y);
        mPath.cubicTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
        mPath.cubicTo(p4.x, p4.y, p5.x, p5.y, p6.x, p6.y);
        mPath.cubicTo(p7.x, p7.y, p8.x, p8.y, p9.x, p9.y);
        mPath.cubicTo(p10.x, p10.y, p11.x, p11.y, p0.x, p0.y);
        canvas.drawPath(mPath, mPaint);
    }

    public void start() {
        setVisibility(VISIBLE);
        if (!dropAnimator.isRunning() || !pullAnimator.isRunning()) {
            dropAnimator.start();
        }
    }

    public void finish() {
        pullAnimator.cancel();
        dropAnimator.cancel();
        radioAnimator.start();
        isEnd = true;
    }

    public void pause() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            pullAnimator.pause();
            dropAnimator.pause();
            radioAnimator.pause();
        }

    }

    private class CirclePoint {
        float x;
        float y;

        CirclePoint(float x, float y) {
            this.x = x;
            this.y = y;
        }

        void setPoint(float x, float y) {
            this.x = x;
            this.y = y;
        }

    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        dropAnimator.cancel();
        pullAnimator.cancel();

    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        if (visibility == INVISIBLE || visibility == GONE) {
            pullAnimator.cancel();
            dropAnimator.cancel();
            radioAnimator.cancel();
        }
        super.onVisibilityChanged(changedView, visibility);
    }
}