在寫登陸界面的時(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)方式
如果說單純的使用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è)地方我大量借鑒了
這個(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>