【android UI學習】簡單炫酷Splash界面

先看運行效果

 第一個動畫:繪製六個點旋轉動畫

  • 繪製六個小圓
  1. mCircleColors這裏一共是6中顏色,分別對應6個小圓顏色
  2. 每個圓得角度=  2π/小圓的個數
  3. 每個圓得x座標 = 圓半徑*cos(a) +圓心X座標
  4. 每個圓得y座標 = 圓半徑*sin(a) +圓心Y座標
  5.  每個小圓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);
        }

    }
  • 六個點得旋轉
  1. 動畫得改變範圍0~2π
  2. 設置動畫LinearInterpolator爲線性變化
  3. 設置動畫狀態爲ValueAnimator.INFINITE 一直旋轉
  4. 通過動態改變偏移角度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();
        }

第二個動畫:縮放動畫

  1.  動態改變這個半徑R的值,來達到縮放功能
  2. 動畫的取值範圍是R到0
  3. 通過動態修改六個圓的繪製半徑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();

        }

第三個動畫:水波紋擴散動畫

  1.  圖中R2=屏幕對角線長度/2
  2. R1就是空心圓半徑
  3. 畫筆的寬度strokeWidth(圖中紅色部分)  =  R2 - R1
  4. 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下載地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章