自定義View3——一個(gè)登陸動(dòng)畫ProgressImgView

在寫登陸界面的時(shí)候我就在想一個(gè)問題——怎么樣子才能讓登陸的等待變得有所期待?自然是在登陸成功的時(shí)候直接就給用戶直接展示應(yīng)用的一個(gè)主頁。所以基于這個(gè)思想,融合了之前看到的一些關(guān)于偽3D動(dòng)畫的Demo,寫了這個(gè)登陸動(dòng)畫。
先看效果圖:


整體思路

這個(gè)效果可以把它分解成以下兩個(gè)部分:
(其實(shí)使用這種的實(shí)現(xiàn)方式是就我個(gè)人目前技術(shù)方向上的一個(gè)妥協(xié),因?yàn)槲覀€(gè)人對于ViewGroup的理解還不是特別的透徹)

頁面消失

頁面消失的話其實(shí)原理非常簡單,就是將承載原來所有控件的根ViewGroup設(shè)置為透明,然后在上面附上一層顯示截圖的ImgView,然后控制ImgView的縮小即可

翻轉(zhuǎn)

翻轉(zhuǎn)這個(gè)地方借鑒了這篇博客里面的實(shí)現(xiàn)方式

https://blog.csdn.net/mr_immortalz/article/details/51918560

如果說單純的使用View翻轉(zhuǎn)180度來顯示文字這樣的代碼實(shí)現(xiàn)就會(huì)變的非常復(fù)雜,所以翻轉(zhuǎn)部分分成了兩塊:

  • 原來的View翻轉(zhuǎn)90度
  • 新的TextView翻轉(zhuǎn)-90度

在代碼構(gòu)成中會(huì)詳細(xì)講解

代碼構(gòu)成

這個(gè)效果的我把它設(shè)計(jì)成了一個(gè)小的VVM模式,總共分成了兩部分

  • 控制動(dòng)畫的ProgressControl
  • 重寫的ProgressImgView
  • 動(dòng)畫計(jì)時(shí)器Rotate3dAnimation
    接下來分開來講這幾個(gè)部分的作用
ProgressControl

ProgressControl正如其名,是用作整個(gè)動(dòng)畫的控制,承擔(dān)了ViewModel的作用。這個(gè)類的設(shè)計(jì)非常簡單,總共有以下幾個(gè)方法:

    public void setText(String text, @ColorInt int color)

    public void setProgress(final int progress)

因?yàn)槭菍iT為登錄這一個(gè)場景設(shè)計(jì)的,所以并沒有設(shè)計(jì)start或者是stop方法,以Progress代替。

ProgressImgView

這個(gè)View繼承自android.support.v7.widget.AppCompatImageView目的是為了使得顯示的圖片有一個(gè)根據(jù)Progress消失的效果,所以在使用的時(shí)候只需要使用這個(gè)方法傳入對應(yīng)的百分比Progress即可

    public void setProgress(int progress)

然后根據(jù)傳入的Progress在onDraw方法中使用clipRect()方法,將canvas剪裁即可

        canvas.save();
        RectF rectF = new RectF(0, 0 + changeHeight * progress, getWidth(), getHeight() - changeHeight * progress);
        canvas.clipRect(rectF);
        super.onDraw(canvas);
        canvas.restore();
Rotate3dAnimation

這個(gè)可以說是整個(gè)動(dòng)畫的核心部分,這個(gè)地方我大量借鑒了

http://www.gcssloop.com/customview/matrix-3d-camera

這個(gè)博客
這個(gè)類繼承自Animation,在它的構(gòu)造方法中需要傳入

    /**
     * 創(chuàng)建一個(gè)繞y軸旋轉(zhuǎn)的3D動(dòng)畫效果,旋轉(zhuǎn)過程中具有深度調(diào)節(jié),可以指定旋轉(zhuǎn)中心。
     *
     * @param context     <------- 添加上下文,為獲取像素密度準(zhǔn)備
     * @param fromDegrees 起始時(shí)角度
     * @param toDegrees   結(jié)束時(shí)角度
     * @param centerX     旋轉(zhuǎn)中心x坐標(biāo)
     * @param centerY     旋轉(zhuǎn)中心y坐標(biāo)
     * @param depthZ      最遠(yuǎn)到達(dá)的z軸坐標(biāo)
     * @param reverse     true 表示由從0到depthZ,false相反
     */
    public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
                             float centerX, float centerY, float depthZ, boolean reverse) {
        mFromDegrees = fromDegrees;
        mToDegrees = toDegrees;
        mCenterX = centerX;
        mCenterY = centerY;
        mDepthZ = depthZ;
        mReverse = reverse;

        // 獲取手機(jī)像素密度 (即dp與px的比例)
        scale = context.getResources().getDisplayMetrics().density;
    }

然后需要重寫applyTransformation方法

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
        float centerX = mCenterX;
        float centerY = mCenterY;
        Camera camera = mCamera;
        final Matrix matrix = t.getMatrix();
        camera.save();

        // 調(diào)節(jié)深度
        if (mReverse) {
            camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
        } else {
            camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
        }

        // 繞X軸旋轉(zhuǎn)
        camera.rotateX(degrees);

        camera.getMatrix(matrix);
        camera.restore();

        // 修正失真,主要修改 MPERSP_0 和 MPERSP_1
        float[] mValues = new float[9];
        matrix.getValues(mValues);                //獲取數(shù)值
        mValues[6] = mValues[6] / scale;            //數(shù)值修正
        mValues[7] = mValues[7] / scale;            //數(shù)值修正
        matrix.setValues(mValues);                //重新賦值

        // 調(diào)節(jié)中心點(diǎn)
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }

更多的詳細(xì)代碼和解釋,以及Camera相關(guān)的可以直接借鑒那篇博客(自己的水平有限,就不誤導(dǎo)了)。

其他設(shè)置

在使用這個(gè)的時(shí)候仍然需要一些其他的設(shè)置,比如說在AndroidManifest中需要設(shè)置這個(gè)Activity的Theme(我已經(jīng)將引用的部分換成了被引用的數(shù)值)

    <style name="transparent" parent="Theme.AppCompat.DayNight.NoActionBar">
        <item name="android:windowBackground">#00ffffff</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
    </style>

在xml中使用的時(shí)候需要將根Root設(shè)置為FrameLayout,以及將自定義的那個(gè)ImgView與實(shí)際承載控件的ViewGroup進(jìn)行重合,這是這個(gè)控件的不足之處,在以后可能會(huì)對這方面進(jìn)行優(yōu)化和改正

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffffff">

    </LinearLayout>

    <ProgressImgView.ProgressImgView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="center"
        android:visibility="invisible" />

</FrameLayout>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容