高仿蘑菇街歡迎頁
蘑菇街歡迎頁
蘑菇街歡迎頁.gif
高仿效果
高仿版本.gif
前言
第一次看蘑菇街歡迎頁的時候,覺得還是挺復雜的一個頁面效果,有點不知從何下手的感覺。不過經過仔細分析后,發現想要實現這種效果,最重要的原理有2點:
1.ViewPager切換時,通過offset偏移量動態修改View元素屬性
2.canvas上細致的控制旋,移,縮,透明等view操作,進行繪制
本文將介紹如何對蘑菇街歡迎頁效果進行分析,拆分,并一步步實現1個高仿版本。
效果拆解
做之前,經過不斷的復看原版效果,分析出其實可以把整體效果拆分為靜態,動態2部分。
整體布局設計.png
-
靜態效果:1個支持4個頁面的ViewPager,每個頁面的展示相對固定,不會根據offset進行改變。
- 第1-4頁的頂部文案
- 第4頁的開始按鈕
-
動態效果:擺放在viewPager上會變形的自定義View,根據offset動態調整需要繪制的元素的寬高,left,top,透明度等。
- 第1頁->第2頁
- 0%->50%,矩形背景高度增加,先上移,再下移
- 0%->50%,模特圖,文案,下移,漸變消失
- 50%-100%,左右裂變出2張背景圖,并左右移開
- 50%->100%,第2頁,頂部,底部圖,漸變顯示
- 50%->100%,第2頁,3張模特圖逐步放大顯示
- 0%->100%,底部背景圖跟隨向左偏移,并消失
- 第2頁->第3頁
- 0%->50%,矩形背景寬度減少,上移
- 0%->50%,頂部,底部圖,3張模特圖漸變消失
- 0%->50%,2張裂變背景圖跟隨向左偏移,并消失
- 50%->100%,第3頁,6張模特圖逐步放大,漸變顯示
- 第3頁->第4頁
- 0%->50%,矩形背景寬度,高度減少,并逆時針進行旋轉
- 0%->50%,6張模特圖縮小,漸變消失
- 50%->100%,左右裂變出2張背景圖,并左右移開
- 50%->100%,頂部模特,文案,漸變顯示
- 50%->100%,底部3長模特圖逐步放大,漸變顯示
- 第1頁->第2頁
實現步驟
分析完整體要做哪些以后,需要分步驟,從簡單的開始一步步實現
1.先把靜態的ViewPager實現
2.根據offset實現矩形背景變化
3.根據offset實現第1頁底部背景,裂變圖變化
4.根據offset實現頁面切換時,每個頁面圖片元素的隱藏,顯示,變形等效果
- 先把靜態的ViewPager實現
自定義ViewPager,每個頁面是一個獨立layout,可以自由實現頂部文案,和最后一個頁面Button
public class MoguViewPager extends RelativeLayout {
private MoguViewPagerAdapter mAdapter;
private ViewPager mViewPager;
private List<View> mViewList = new ArrayList<>();
/** 每個頁面都是一個layout */
private int[] mLayouts = new int[] {R.layout.guide_view_one, R.layout.guide_view_two, R.layout.guide_view_three,
R.layout.guide_view_four};
public MoguViewPager(Context context) {
super(context);
init();
}
public MoguViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
inflate(getContext(), R.layout.layout_mogu_viewpager, this);
mViewPager = (ViewPager) this.findViewById(R.id.viewpager);
{
/** 初始化4個頁面 */
for (int i = 0; i < mLayouts.length; i++) {
View view = View.inflate(getContext(), mLayouts[i], null);
mViewList.add(view);
}
}
mAdapter = new MoguViewPagerAdapter(mViewList, getContext());
mViewPager.setAdapter(mAdapter);
}
}
xml布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:clipChildren="false"/>
<!--這里準備放個自定義View-->
</RelativeLayout>
第一步完成,實現還是比較簡單的,直接看效果:
第1版.gif
- 根據offset實現矩形背景變化
自定義會變形的TransforView,在xml布局中擺放在ViewPager之上
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:clipChildren="false"/>
<com.listen.test_mogu_viewpager.viewpager.TransforView
android:id="@+id/transfor_view" android:layout_width="match_parent"
android:layout_height="450dp"
android:layout_centerInParent="true"/>
</RelativeLayout>
給ViewPager添加addOnPageChangeListener(),在onPageScrolled()的時候將position,positionOffset,positionOffsetPixels傳遞給TransforView。
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mTransforView.transfor(position, positionOffset, positionOffsetPixels);
}
});
在TransforView中,首先定義頁面切換時變化的參數,比如第1頁->第2頁會發生的變化有高度放大40%,上移30dp,下移60dp,則只需要定義0.4,-30dp,60dp三個參數即可。
/**
* 第1頁->第2頁
* 0%->50%,矩形背景高度增加40%,先上移30dp,再下移60dp
*/
public static final float FIRST_HEIGHT = 0.4f;// 第1個頁面高度縮放比例,正:放大,負:縮小
public final int FIRST_TOP1 = -dp2px(30);// 第1個頁面top移動距離,正:下移,負:上移
public final int FIRST_TOP2 = dp2px(60);// 第1個頁面top移動距離,正:下移,負:上移
public static final float FIRST_RATE = 0.5f;// 在偏移50%處,進行下一頁的顯示
/**
* 第2頁->第3頁
* 0%->50%,矩形背景寬度減少15%,上移20dp
*/
public static final float SECOND_WIDTH = -0.15f;// 第2個頁面寬度縮放比例,正:放大,負:縮小
public final int SECOND_TOP = -dp2px(20);// 第2個頁面top移動距離比例,正:下移,負:上移
public static final float SECOND_RATE = 0.5f;// 在偏移50%處,進行下一頁的顯示
/**
* 第3頁->第4頁
* 0%->50%,矩形背景寬度,高度減少10%,并逆時針進行旋轉10度
*/
public static final float THIRD_WIDTH = -0.1f;// 第3個頁面寬度縮放比例,正:放大,負:縮小
public static final float THIRD_HEIGHT = -0.1f;// 第3個頁面高度縮放比例,正:放大,負:縮小
public static final int THIRD_DEGREE = -10;// 第3個頁面角度調整,正:順時針,負:逆時針
public static final float THIRD_RATE = 0.5f;// 在偏移50%處,進行下一頁的顯示
/**
* 第1頁初始化矩形背景的寬,高,left,top
*/
private float mPage1RectBgDefaultWidth = dp2px(260);
private float mPage1RectBgDefaultHeight = dp2px(230);
private float mPage1RectBgDefaultLeft = getScreenWidth() / 2 - mPage1RectBgDefaultWidth / 2;//left=屏幕寬度/2-矩形寬度/2
private float mPage1RectBgDefaultTop = dp2px(80);
/**
* 第1頁->第2頁
* 在第1頁的基礎上進行變化
* 1.height放大
* 2.top先上移n,在下移n*2
*/
private float mPage2RectBgDefaultWidth = mPage1RectBgDefaultWidth;
private float mPage2RectBgDefaultHeight = mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT);// 第2頁的高度=第一頁高度*1.4
private float mPage2RectBgDefaultLeft = mPage1RectBgDefaultLeft;
private float mPage2RectBgDefaultTop = mPage1RectBgDefaultTop + FIRST_TOP1 + FIRST_TOP2;//第2頁的top=第一頁的top-30dp+60dp
/**
* 第2頁->第3頁
* 在第2頁的基礎上進行變化
* 1.寬度縮小
* 2.top上移
*/
private float mPage3RectBgDefaultWidth = mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH);
private float mPage3RectBgDefaultHeight = mPage2RectBgDefaultHeight;
private float mPage3RectBgDefaultLeft = getScreenWidth() / 2 - mPage3RectBgDefaultWidth / 2;//第3頁的left=屏幕的寬度/2-矩形背景寬度/2
private float mPage3RectBgDefaultTop = mPage2RectBgDefaultTop + SECOND_TOP;
/**
* 第3頁->第4頁
* 在第3頁的基礎上進行變化
* 1.寬度縮小
* 2.高度縮小
* 2.逆時針旋轉
*/
private float mPage4RectBgDefaultWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH);
private float mPage4RectBgDefaultHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT);
private float mPage4RectBgDefaultLeft = getScreenWidth() / 2 - mPage4RectBgDefaultWidth / 2;
private float mPage4RectBgDefaultTop = mPage3RectBgDefaultTop;
private float mPage4ModelDefaultWidth = (mPage4RectBgDefaultWidth - padding() * 4) / 3;
TransforView的transfor()負責接收position,positionOffset,positionOffsetPixels,并根據position判斷當前第幾頁,從而決定要實現哪些效果,通過positionOffset計算view的寬,高,left,top變化比例,實現縮小,放大,左右,上下移動,旋轉等效果
public void transfor(int position, float positionOffset, int positionOffsetPixels) {
mCurrentPageIndex = position;
if (fromPage1ToPage2(position)) {
if (positionOffset < FIRST_RATE) {
/** 第1頁,在0->50%區間偏移 */
/** 矩形背景,高度放大40% */
/**
* 偏移到50%的時候height需要放大40%,defaultHeight=400,targetHeight=400*1.4=560
*
* offset=0
* 400 * (1 + 0.4 * 0 * (1 / 0.5)) = 400
*
* offset=0.25
* 400 * (1 + 0.4 * 0.25 * (1 / 0.5)) = 400 * 1.2 = 480
*
* offset=0.5
* 400 * (1 + 0.4 * 0.5 * (1 / 0.5)) = 400 * 1.4 = 560
*
*/
mRectBgCurrentHeight =
(int) (mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT * positionOffset * (1 / FIRST_RATE)));
/** 矩形背景,向上移動30dp */
mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE)));
} else {
/** 第1頁,在50%->100%區間偏移 */
/** 矩形背景,上移30dp后,向下偏移60dp */
mRectBgCurrentTop =
(int) (mPage1RectBgDefaultTop + FIRST_TOP1 + (FIRST_TOP2 * (positionOffset - FIRST_RATE) * 1.0 / (1 - FIRST_RATE)));
}
} else if (fromPage2ToPage3(position)) {
/** 矩形背景,寬度縮小15% */
mRectBgCurrentWidth = (int) (mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH * positionOffset));
mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2;
/** 矩形背景,上移20dp */
mRectBgCurrentTop = (int) (mPage2RectBgDefaultTop + (SECOND_TOP * positionOffset));
} else if (fromPage3ToPage4(position)) {
/** 背景矩形的寬度,減少10% */
mRectBgCurrentWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH * positionOffset);
mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2;
/** 背景矩形的高度,減少10% */
mRectBgCurrentHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT * positionOffset);
/** 逆時針旋轉10度 */
mRectBgCurrentDegree = THIRD_DEGREE * positionOffset;
}
/** 請求重新繪制 */
postInvalidate();
}
最后在onDraw方法中,將計算好寬,高,left,top的view在繪制在canvas上
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect = new RectF();
rect.left = mRectBgCurrentLeft;
rect.top = mRectBgCurrentTop;
rect.right = rect.left + mRectBgCurrentWidth;
rect.bottom = rect.top + mRectBgCurrentHeight;
canvas.rotate(mRectBgCurrentDegree, rect.left + mRectBgCurrentWidth / 2, rect.top + mRectBgCurrentHeight / 2);
canvas.drawRoundRect(rect, mRectBgDefaultCorner, mRectBgDefaultCorner, mRectBgPaint);
}
第2步實現了通過ViewPager的偏移offset,動態修改矩形背景的寬,高,left,top,和角度,效果如下:
第2版.gif
- 根據offset實現第1頁底部背景,裂變圖變化
在TransforView的init()初始化方法中,獲取并設置圖片的默認寬,高,left,top。這里封裝了1個ViewModel,里面記錄了在canvas上繪制圖形需要的bitmap,paint,matrix,width,height,left,top等屬性,在調用ViewModel.create()的時候,通過Matrix將Bitmap縮放到合適的寬高,才能通過寬高計算left,top,以便在矩形背景上進行精確的繪制。
public ViewModel create() {
/** 縮放圖片尺寸到合適的比例 */
matrix.postScale(currentWidth / bitmap.getWidth(), currentHeight / bitmap.getHeight());
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return this;
}
private void init() {
/** 第1頁,底部背景圖 */
mPage1BottomBg =
new ViewModel(getContext(), R.drawable.one_bottom_bg).alpha(255)
.width(mPage1RectBgDefaultWidth - padding() * 2)
.left(mPage1RectBgDefaultLeft + padding())
// top距離=矩形背景top+height+5dp邊距
.top(mPage1RectBgDefaultTop + mPage1RectBgDefaultHeight + padding())
.create();
/** 第2頁,裂變背景圖 */
for (int i = 0; i < 2; i++) {
mPage2Split[i] =
new ViewModel(getContext(), R.drawable.two_bg).width(mPage2RectBgDefaultWidth)
.height(mPage2RectBgDefaultHeight)
.left(mPage2RectBgDefaultLeft)
.top(mPage2RectBgDefaultTop)
.create();
}
/** 第4頁,2張裂變背景圖 */
for (int i = 0; i < mPage4Split.length; i++) {
mPage4Split[i] =
new ViewModel(getContext(), R.drawable.four_bg)
.width(mPage4RectBgDefaultWidth)
.height(mPage4RectBgDefaultHeight)
.left(mPage4RectBgDefaultLeft)
.top(mPage4RectBgDefaultTop);
}
}
在transfor()中根據偏移offset,修改圖片位置,實現移動;第1頁的背景圖,offset多少則左移多少即可,第2頁的裂變圖,在第1頁滑動到50%時,顯示裂變背景圖,根據offset分別左右平移,第4頁裂變圖原理一致,只是繪制前需要通過Matrix將圖進行旋轉。
private void transfor(int position, float positionOffset, int positionOffsetPixels) {
if (fromPage1ToPage2(position)) {
/** 第1頁,底部背景圖,根據頁面向左偏移 */
mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels;
if (positionOffset < FIRST_RATE) {
} else {
/** 第2頁,計算裂變背景圖的偏移px,并修改透明度漸變顯示 */
float offset = (mPage1RectBgDefaultWidth + dp2px(15)) * ((positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)));
mPage2Split[0].currentLeft = mPage2Split[0].defaultLeft - offset;
mPage2Split[1].currentLeft = mPage2Split[0].defaultLeft + offset;
/**
* 偏移到50%的時候alpha需要為0,偏移到100%,alpha需要為255,不過此時positionOffset的取值=0.5~1
*
* offset=0.5
* 255 * (0.5 - 0.5) * (1 / (1 - 0.5)))=255 * 0 = 0
*
* offset=0.75
* 255 * (0.75 - 0.5) * (1 / (1 - 0.5)))=255 * 0.5 = 127.5
*
* offset=1
* 255 * (1 - 0.5) * (1 / (1 - 0.5)))=255 * 1 = 255
*/
mPage2Split[0].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
mPage2Split[1].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
}
} else if (fromPage2ToPage3(position)) {
if (positionOffset < SECOND_RATE) {
}
} else if (fromPage3ToPage4(position)) {
if (positionOffset < THIRD_RATE) {
} else {
/** 顯示第4頁,裂變背景圖,并向左右平移 */
float offset = (mPage4RectBgDefaultWidth + dp2px(40)) * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)));
for (int i = 0; i < mPage4Split.length; i++) {
mPage4Split[i].matrix.reset();
mPage4Split[i].matrix.postScale(mPage4RectBgDefaultWidth / mPage4Split[i].bitmap.getWidth(), mPage4RectBgDefaultHeight / mPage4Split[i].bitmap.getHeight());
float currentLeft = 0;
if (i == 0) {
// 左移
currentLeft = mPage4RectBgDefaultLeft - offset;
} else if (i == 1) {
// 右移
currentLeft = mPage4RectBgDefaultLeft + offset;
}
// 平移
mPage4Split[i].matrix.postTranslate(currentLeft, mPage4RectBgDefaultTop);
// 旋轉角度
mPage4Split[i].matrix.postRotate(THIRD_DEGREE, currentLeft + mPage4RectBgDefaultWidth/2,
mPage4RectBgDefaultTop + mPage4RectBgDefaultHeight/2);
mPage4Split[i].alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)))));
}
}
}
}
效果如下:
第3版.gif
- 根據offset實現頁面切換時,每個頁面圖片元素的隱藏,顯示,變形等效果
架子已經搭好,接下來就是實現每個頁面的子view在切換時的效果變化,其實原理就是根據offset偏移比例,不斷的計算view的寬高,實現縮放;計算left,top,實現移動;計算alpha,實現顯示,隱藏。
以下是第1頁->第2頁時主要的代碼,其他頁面切換效果原理大致一致,只是算法不同,源碼上都有詳細的注釋,感興趣的朋友可以直接上 github 下載。
/**
* 在第1頁從0%->50%的過程中,修改透明度,逐漸隱藏第1頁的頂部,底部圖
*
**/
private void stepByHidePage1Views(float positionOffset) {
/**
* 偏移到50%的時候alpha需要為0,view不可見
*
* offset=0
* 255-(255*0.0*(1/0.5)) = 0
*
* offset=0.25
* 255-(255*0.25*(1/0.5)) = 127
*
* offset=0.5
* 255-(255*0.5*(1/0.5)) = 255
*/
mPage1Top.alpha((int) (255 - (255 * positionOffset * (1 / FIRST_RATE))));
mPage1Bottom.alpha((int) (255 - (255 * positionOffset * (1 / FIRST_RATE))));
/** 第1頁,頂部圖向下移動 */
mPage1Top.currentTop = mPage1Top.defaultTop + (FIRST_TOP2 + FIRST_TOP1) * positionOffset * (1 / FIRST_RATE);
/** 第1頁,底部圖跟隨頂部圖向下移動 */
mPage1Bottom.currentTop = mPage1Top.currentTop + mPage1Top.defaultHeight + padding();
}
/**
* 在第1頁從50%->100%的過程中,逐漸隱藏第2頁的頂部,底部圖片,
* 并控制中間3張模特圖,依據上1張狀態實現放大展示
**/
private void stepByShowPage2Views(float positionOffset) {
/** 第2頁,頂部圖,跟隨矩形背景下移 */
mPage2Top.currentTop = mRectBgCurrentTop + padding();
/** 第2頁,底部圖,跟隨矩形背景下移 */
mPage2Bottom.currentTop = mPage2Center[0].currentTop + mPage2Center[0].defaultHeight + padding();
/** 第2頁,計算裂變背景圖的偏移px,并修改透明度漸變顯示 */
float offset =
(mPage1RectBgDefaultWidth + dp2px(15)) * ((positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE)));
mPage2Split[0].currentLeft = mPage2Split[0].defaultLeft - offset;
mPage2Split[1].currentLeft = mPage2Split[0].defaultLeft + offset;
/**
* 偏移到50%的時候alpha需要為0,偏移到100%,alpha需要為255,不過此時positionOffset的取值=0.5~1
*
* offset=0.5
* 255 * (0.5 - 0.5) * (1 / (1 - 0.5)))=255 * 0 = 0
*
* offset=0.75
* 255 * (0.75 - 0.5) * (1 / (1 - 0.5)))=255 * 0.5 = 127.5
*
* offset=1
* 255 * (1 - 0.5) * (1 / (1 - 0.5)))=255 * 1 = 255
*/
mPage2Split[0].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
mPage2Split[1].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
/** 第2頁,頂部,底部圖,透明度漸變顯示,偏移量達到100%,完成顯示 */
mPage2Top.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
mPage2Bottom.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
/** 第2頁,顯示中間3張模特圖 */
for (int i = 0; i < mPage2Center.length; i++) {
if (i == 0) {
/** 第2頁,顯示第1張模特圖 */
mPage2Center[i].currentWidth =
mPage2Center[i].defaultWidth * (positionOffset - FIRST_RATE) * 1 / (1 - FIRST_RATE);
mPage2Center[i].currentHeight =
mPage2Center[i].defaultHeight * (positionOffset - FIRST_RATE) * 1 / (1 - FIRST_RATE);
mPage2Center[i].alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
mPage2Center[i].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding();
} else {
/** 第2,3張模特圖,在前1張顯示到一半時才顯示 */
if (mPage2Center[i - 1].currentWidth >= mPage2Center[i - 1].defaultWidth / 2) {
float rate = mPage2Center[i - 1].widthRate() - 0.5f;
mPage2Center[i].currentWidth = mPage2Center[i].defaultWidth * (rate * 2);
mPage2Center[i].currentHeight = mPage2Center[i].defaultHeight * (rate * 2);
/** 第2,3張模特圖,需要根據第1張圖計算left */
mPage2Center[i].currentLeft =
mPage2Center[0].currentLeft + mPage2Center[0].currentWidth + padding();
mPage2Center[i].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding();
if (i == 2) {
/** 第3張模特圖,根據第2張圖計算top */
mPage2Center[i].currentTop =
mPage2Center[1].currentTop + mPage2Center[1].currentHeight + padding();
}
mPage2Center[i].alpha((int) (255 * (positionOffset * rate * 2)));
} else {
mPage2Center[i].alpha(0);
}
}
}
}
private void tranfor(int position, float positionOffset, int positionOffsetPixels) {
if (positionOffset <= 0) {
return;
}
if (fromPage1ToPage2(position)) {
/** 第1頁,底部背景圖,根據頁面向左偏移 */
mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels;
if (positionOffset < FIRST_RATE) {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里重新設置一次,保證第2頁的view不可見 */
hidePage2Views();
/** 第1頁,在0->50%區間偏移 */
/** 矩形背景,高度放大40%,向上移動30dp */
transformRectBgFrom1To2Before(positionOffset);
/** 第1頁,漸漸隱頂部圖,底部圖;透明度漸變消失,偏移到50%時完全消失 */
stepByHidePage1Views(positionOffset);
} else {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里調重新設置一次。保證第1頁的view不可見 */
hidePage1Views();
/** 第1頁,在50%->100%區間偏移 */
/** 矩形背景,上移30dp后,向下偏移60dp */
transformRectBgFrom1To2After(positionOffset);
/** 第2頁,漸漸顯示頂部,3張模特圖,底部圖 */
stepByShowPage2Views(positionOffset);
}
} else if (fromPage2ToPage3(position)) {
/** 矩形背景,寬度縮小15%,上移20dp */
transformRectBgFrom2To3(positionOffset);
if (positionOffset < SECOND_RATE) {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里調重新設置一次。保證第3頁滑回到第2頁時,第3頁的view不可見 */
hidePage3Views();
/** 第2頁,在0->50%區間偏移,漸漸隱藏頂部,中間,底部,裂變背景圖 */
stepByHidePage2Views(positionOffset, positionOffsetPixels);
} else {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里調重新設置一次,保證第2頁的view不可見 */
hidePage2Views();
/** 第2頁,在50->100%區間偏移,漸漸顯示第3頁,6張模特圖 */
stepByShowPage3Views(positionOffset);
}
} else if (fromPage3ToPage4(position)) {
/** 背景矩形的寬度,高度減少10%,逆時針旋轉10度 */
transformRectBgFrom3To4(positionOffset);
if (positionOffset < THIRD_RATE) {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里調重新設置一次,保證第4頁的view不可見 */
hidePage4Views();
/** 漸漸縮放,隱藏第3頁,6張模特圖 */
stepByHidePage3Views(positionOffset);
} else {
/** 快速滑動的時候,可能丟失最后一次繪制,所以需要在這里調重新設置一次,保證第3頁的view縮放完成 */
scaleHidePage3Views();
/** 漸漸顯示第4頁,頂部圖,底部3張模特圖,分裂背景圖 */
stepByShowPage4Views(positionOffset);
}
}
postInvalidate();
}
目前只仿了80%左右,還有一些細節的效果,以及適配,性能調優還沒實現。其實真正的難點在于如何精細化的控制每個view的屬性,因為頁面中每個圖片的位置,大小都是在參照其他view的基礎上進行計算后得出的。