高仿蘑菇街歡迎頁(yè)

蘑菇街歡迎頁(yè)

蘑菇街歡迎頁(yè).gif
蘑菇街歡迎頁(yè).gif

高仿效果

高仿版本.gif
高仿版本.gif

這里這里...Demo下載地址

前言

本文將介紹如何對(duì)蘑菇街歡迎頁(yè)效果進(jìn)行分析旦装,拆分券腔,并一步步實(shí)現(xiàn)1個(gè)高仿版本参滴,最重要的設(shè)計(jì)思路包括以下2點(diǎn):
1.ViewPager切換時(shí),通過(guò)offset偏移量動(dòng)態(tài)修改View元素屬性
2.canvas上精細(xì)化的控制旋役纹,移枯冈,縮魄健,透明等view屬性變化,進(jìn)行動(dòng)態(tài)繪制

效果拆解

首先可以把整體效果拆分為靜態(tài)瓦盛,動(dòng)態(tài)2部分绕德。

整體布局設(shè)計(jì).png
整體布局設(shè)計(jì).png
  • 靜態(tài):1個(gè)支持4個(gè)頁(yè)面的ViewPager,每個(gè)頁(yè)面的展示相對(duì)固定,不會(huì)根據(jù)offset進(jìn)行改變。

    • 第1-4頁(yè)的頂部文案
    • 第4頁(yè)的開(kāi)始按鈕
  • 動(dòng)態(tài):擺放在viewPager上會(huì)變形的自定義View重窟,根據(jù)offset動(dòng)態(tài)調(diào)整需要繪制的元素的寬高顽分,left梯皿,top,透明度等盟萨。

    • 第1頁(yè)->第2頁(yè)
      • 0%->50%,矩形背景高度增加,先上移梳玫,再下移
      • 0%->50%偿荷,模特圖,文案芯侥,下移仰楚,漸變消失
      • 50%-100%,左右裂變出2張背景圖捉貌,并左右移開(kāi)
      • 50%->100%罢浇,第2頁(yè)色乾,頂部金砍,底部圖,漸變顯示
      • 50%->100%笤闯,第2頁(yè)棍厂,3張模特圖逐步放大顯示
      • 0%->100%颗味,底部背景圖跟隨向左偏移,并消失
    • 第2頁(yè)->第3頁(yè)
      • 0%->50%牺弹,矩形背景寬度減少浦马,上移
      • 0%->50%,頂部张漂,底部圖晶默,3張模特圖漸變消失
      • 0%->50%,2張裂變背景圖跟隨向左偏移航攒,并消失
      • 50%->100%磺陡,第3頁(yè),6張模特圖逐步放大,漸變顯示
    • 第3頁(yè)->第4頁(yè)
      • 0%->50%币他,矩形背景寬度坞靶,高度減少,并逆時(shí)針進(jìn)行旋轉(zhuǎn)
      • 0%->50%圆丹,6張模特圖縮小滩愁,漸變消失
      • 50%->100%躯喇,左右裂變出2張背景圖辫封,并左右移開(kāi)
      • 50%->100%,頂部模特廉丽,文案倦微,漸變顯示
      • 50%->100%,底部3長(zhǎng)模特圖逐步放大正压,漸變顯示

以上是對(duì)部分實(shí)現(xiàn)細(xì)節(jié)的分析欣福,抽取焦履;本文demo會(huì)全部實(shí)現(xiàn)以上變化效果拓劝。

實(shí)現(xiàn)步驟

1.實(shí)現(xiàn)靜態(tài)的ViewPager
2.根據(jù)offset實(shí)現(xiàn)矩形背景變化
3.根據(jù)offset實(shí)現(xiàn)第1頁(yè)底部背景,第2嘉裤,4頁(yè)裂變背景圖變化
4.根據(jù)offset實(shí)現(xiàn)頁(yè)面切換時(shí)郑临,每個(gè)頁(yè)面圖片元素的隱藏,顯示屑宠,變形等效果

  • 實(shí)現(xiàn)靜態(tài)的ViewPager

自定義ViewPager厢洞,每個(gè)頁(yè)面是一個(gè)獨(dú)立layout,可以自由實(shí)現(xiàn)每個(gè)頁(yè)面的頂部文案典奉,和第4個(gè)頁(yè)面的Button

public class MoguViewPager extends RelativeLayout {

    private MoguViewPagerAdapter mAdapter;
    private ViewPager mViewPager;
    private List<View> mViewList = new ArrayList<>();
    /** 每個(gè)頁(yè)面都是一個(gè)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個(gè)頁(yè)面 */
            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);
    }

}
<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"/>

    <!--這里準(zhǔn)備放個(gè)自定義View-->
</RelativeLayout>

第一步完成躺翻,實(shí)現(xiàn)代碼還是比較簡(jiǎn)單的,直接看效果:


第1版.gif
第1版.gif
  • 根據(jù)offset實(shí)現(xiàn)矩形背景變化

自定義會(huì)變形的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()監(jiān)聽(tīng)公你,在onPageScrolled()的時(shí)候?qū)osition,positionOffset假瞬,positionOffsetPixels傳遞給TransforView陕靠。

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                mTransforView.transfor(position, positionOffset, positionOffsetPixels);
            }
        });

在TransforView中,首先定義頁(yè)面切換時(shí)變化的參數(shù)笨触,比如第1頁(yè)->第2頁(yè)切換時(shí)懦傍,第1頁(yè)的矩形背景高度放大40%,上移30dp芦劣,下移60dp粗俱,則只需要定義FIRST_HEIGHT=0.4,F(xiàn)IRST_TOP1=-30dp虚吟,F(xiàn)IRST_TOP2 =60dp三個(gè)參數(shù)即可寸认。


/**
 * 第1頁(yè)->第2頁(yè)
 * 0%->50%签财,矩形背景高度增加40%,先上移30dp偏塞,再下移60dp
 */
public static final float FIRST_HEIGHT = 0.4f;// 第1個(gè)頁(yè)面高度縮放比例唱蒸,正:放大,負(fù):縮小
public final int FIRST_TOP1 = -dp2px(30);// 第1個(gè)頁(yè)面top移動(dòng)距離灸叼,正:下移神汹,負(fù):上移
public final int FIRST_TOP2 = dp2px(60);// 第1個(gè)頁(yè)面top移動(dòng)距離,正:下移古今,負(fù):上移
public static final float FIRST_RATE = 0.5f;// 在偏移50%處屁魏,進(jìn)行下一頁(yè)的顯示
/**
 * 第2頁(yè)->第3頁(yè)
 * 0%->50%,矩形背景寬度減少15%捉腥,上移20dp
 */
public static final float SECOND_WIDTH = -0.15f;// 第2個(gè)頁(yè)面寬度縮放比例氓拼,正:放大,負(fù):縮小
public final int SECOND_TOP = -dp2px(20);// 第2個(gè)頁(yè)面top移動(dòng)距離比例抵碟,正:下移桃漾,負(fù):上移
public static final float SECOND_RATE = 0.5f;// 在偏移50%處,進(jìn)行下一頁(yè)的顯示
/**
 * 第3頁(yè)->第4頁(yè)
 * 0%->50%拟逮,矩形背景寬度撬统,高度減少10%,并逆時(shí)針進(jìn)行旋轉(zhuǎn)10度
 */
public static final float THIRD_WIDTH = -0.1f;// 第3個(gè)頁(yè)面寬度縮放比例唱歧,正:放大宪摧,負(fù):縮小
public static final float THIRD_HEIGHT = -0.1f;// 第3個(gè)頁(yè)面高度縮放比例,正:放大颅崩,負(fù):縮小
public static final int THIRD_DEGREE = -10;// 第3個(gè)頁(yè)面角度調(diào)整几于,正:順時(shí)針,負(fù):逆時(shí)針
public static final float THIRD_RATE = 0.5f;// 在偏移50%處沿后,進(jìn)行下一頁(yè)的顯示

/**
 * 第1頁(yè)初始化矩形背景的寬沿彭,高,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頁(yè)->第2頁(yè)
 * 在第1頁(yè)的基礎(chǔ)上進(jìn)行變化
 * 1.height放大
 * 2.top先上移n喉刘,在下移n*2
 */
private float mPage2RectBgDefaultWidth = mPage1RectBgDefaultWidth;
private float mPage2RectBgDefaultHeight = mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT);// 第2頁(yè)的高度=第一頁(yè)高度*1.4
private float mPage2RectBgDefaultLeft = mPage1RectBgDefaultLeft;
private float mPage2RectBgDefaultTop = mPage1RectBgDefaultTop + FIRST_TOP1 + FIRST_TOP2;//第2頁(yè)的top=第一頁(yè)的top-30dp+60dp

/**
 * 第2頁(yè)->第3頁(yè)
 * 在第2頁(yè)的基礎(chǔ)上進(jìn)行變化
 * 1.寬度縮小
 * 2.top上移
 */
private float mPage3RectBgDefaultWidth = mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH);
private float mPage3RectBgDefaultHeight = mPage2RectBgDefaultHeight;
private float mPage3RectBgDefaultLeft = getScreenWidth() / 2 - mPage3RectBgDefaultWidth / 2;//第3頁(yè)的left=屏幕的寬度/2-矩形背景寬度/2
private float mPage3RectBgDefaultTop = mPage2RectBgDefaultTop + SECOND_TOP;

/**
 * 第3頁(yè)->第4頁(yè)
 * 在第3頁(yè)的基礎(chǔ)上進(jìn)行變化
 * 1.寬度縮小
 * 2.高度縮小
 * 2.逆時(shí)針旋轉(zhuǎn)
 */
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()方法負(fù)責(zé)接收position,positionOffset漆弄,
positionOffsetPixels睦裳,并根據(jù)position判斷當(dāng)前第幾頁(yè),從而決定要實(shí)現(xiàn)哪些效果撼唾。比如在第1頁(yè)->第2頁(yè)的0%-50區(qū)間時(shí)廉邑,需要將高度放大40%:mRectBgCurrentHeight =(int) (mPage1RectBgDefaultHeight * (1 + FIRST_HEIGHT * positionOffset * (1 / FIRST_RATE)))。mRectBgCurrentHeight是矩形背景當(dāng)前的高度,是個(gè)動(dòng)態(tài)值蛛蒙,mPage1RectBgDefaultHeight是屏幕處于第1頁(yè)時(shí)矩形背景的初始值糙箍,只要基于這個(gè)初始值,根據(jù)positionOffset計(jì)算偏移的比例牵祟,就可以知道當(dāng)前動(dòng)態(tài)的高度值應(yīng)該是多少深夯。

public void transfor(int position, float positionOffset, int positionOffsetPixels) {
    mCurrentPageIndex = position;
    if (fromPage1ToPage2(position)) {
        if (positionOffset < FIRST_RATE) {
            /** 第1頁(yè),在0->50%區(qū)間偏移 */
            /** 矩形背景诺苹,高度放大40% */
            /**
             * 偏移到50%的時(shí)候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)));
            /** 矩形背景筝尾,向上移動(dòng)30dp */
            mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE)));

        } else {
            /** 第1頁(yè)捡需,在50%->100%區(qū)間偏移 */

            /** 矩形背景,上移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);

        /** 逆時(shí)針旋轉(zhuǎn)10度 */
        mRectBgCurrentDegree = THIRD_DEGREE * positionOffset;
    }
     /** 請(qǐng)求重新繪制 */
    postInvalidate();
}

最后在onDraw方法中殊霞,調(diào)用canvas.drawRoundRect()將計(jì)算好寬摧阅,高,left绷蹲,top的圓角矩形在繪制在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步:通過(guò)ViewPager的偏移offset,實(shí)現(xiàn)了矩形背景在頁(yè)面間切換時(shí)的變化效果祝钢,如下:


第2版.gif
第2版.gif
  • 根據(jù)offset實(shí)現(xiàn)第1頁(yè)底部背景比规,第2,4頁(yè)裂變圖背景圖變化

在TransforView的init()初始化方法中拦英,獲取并設(shè)置圖片的默認(rèn)寬蜒什,高,left疤估,top灾常。這里封裝了1個(gè)ViewModel,里面記錄了在canvas上繪制圖形需要的bitmap铃拇,paint钞瀑,matrix,width慷荔,height雕什,left,top等屬性。在調(diào)用ViewModel.create()的時(shí)候监徘,通過(guò)matrix.postScale()將Bitmap縮放一定比例晋修,以便在矩形背景上進(jìn)行精確的繪制,比如:矩形背景的200凰盔,要在1排展示3張圖墓卦,則每張圖的寬度=(200-矩形左邊距-矩形右邊距-中間2張圖的左右邊距)/3。

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頁(yè)户敬,底部背景圖 */
    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頁(yè)落剪,裂變背景圖 */
    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頁(yè),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()中修改圖片left尿庐,top忠怖,實(shí)現(xiàn)移動(dòng);第1頁(yè)的底部背景圖抄瑟,根據(jù)viewPager向左滑動(dòng)的距離凡泣,跟隨左移,直到消失不可見(jiàn)皮假。在第1頁(yè)滑動(dòng)到50%時(shí)鞋拟,顯示第2頁(yè)裂變背景圖,根據(jù)offset分別左右平移惹资,第4頁(yè)裂變圖原理一致贺纲,只是繪制前需要通過(guò)Matrix.postRotate()將圖進(jìn)行旋轉(zhuǎn)。

private void transfor(int position, float positionOffset, int positionOffsetPixels) {
        if (fromPage1ToPage2(position)) {
            /** 第1頁(yè)褪测,底部背景圖猴誊,根據(jù)頁(yè)面pian yi偏移offset向左偏移 */
            mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels;

            if (positionOffset < FIRST_RATE) {

            } else {
                /** 第2頁(yè),計(jì)算裂變背景圖的偏移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%的時(shí)候alpha需要為0懈叹,偏移到100%,alpha需要為255萝毛,不過(guò)此時(shí)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頁(yè)项阴,裂變背景圖,并向左右平移 */
                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);
                    // 旋轉(zhuǎn)角度
                    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
第3版.gif
  • 4個(gè)頁(yè)面切換時(shí)笆包,實(shí)現(xiàn)每個(gè)頁(yè)面圖片元素的隱藏环揽,顯示,變形等效果

在transfor()中庵佣,根據(jù)position判斷當(dāng)前頁(yè)數(shù)歉胶,才知道當(dāng)前是從第幾頁(yè)滑動(dòng)到第幾頁(yè),該隱藏巴粪,或顯示哪些view通今。

private void transfor(int position, float positionOffset, int positionOffsetPixels) {
    if (fromPage1ToPage2(position)) {
        /** 第1頁(yè)粥谬,底部背景圖,根據(jù)頁(yè)面向左偏移 */
        mPage1BottomBg.currentLeft = mPage1BottomBg.defaultLeft - positionOffsetPixels;

        if (positionOffset < FIRST_RATE) {
        
            /** 第1頁(yè)辫塌,在0->50%區(qū)間偏移 */
            /** 矩形背景漏策,高度放大40%,向上移動(dòng)30dp */
            transformRectBgFrom1To2Before(positionOffset);

            /** 第1頁(yè)臼氨,漸漸隱頂部圖掺喻,底部圖;透明度漸變消失储矩,偏移到50%時(shí)完全消失 */
            stepByHidePage1Views(positionOffset);

        } else {

            /** 第1頁(yè)感耙,在50%->100%區(qū)間偏移 */
            /** 矩形背景,上移30dp后持隧,向下偏移60dp */
            transformRectBgFrom1To2After(positionOffset);

            /** 第2頁(yè)即硼,漸漸顯示頂部,3張模特圖屡拨,底部圖 */
            stepByShowPage2Views(positionOffset);

        }
    } else if (fromPage2ToPage3(position)) {
            /** 矩形背景只酥,寬度縮小15%,上移20dp */
           transformRectBgFrom2To3(positionOffset);
    
           if (positionOffset < SECOND_RATE) {
               /** 第2頁(yè)洁仗,在0->50%區(qū)間偏移层皱,漸漸隱藏頂部,中間赠潦,底部,裂變背景圖 */
               stepByHidePage2Views(positionOffset, positionOffsetPixels);
           } else {
               /** 第2頁(yè)草冈,在50->100%區(qū)間偏移她奥,漸漸顯示第3頁(yè),6張模特圖 */
               stepByShowPage3Views(positionOffset);
           }
    } else if (fromPage3ToPage4(position)) {
            /** 背景矩形的寬度怎棱,高度減少10%哩俭,逆時(shí)針旋轉(zhuǎn)10度 */
            transformRectBgFrom3To4(positionOffset);

            if (positionOffset < THIRD_RATE) {
                /** 漸漸縮放,隱藏第3頁(yè)拳恋,6張模特圖 */
                stepByHidePage3Views(positionOffset);

            } else {
                /** 漸漸顯示第4頁(yè)凡资,頂部圖,底部3張模特圖谬运,分裂背景圖 */
                stepByShowPage4Views(positionOffset);
            }
    }
}

第1頁(yè)->第2頁(yè)隙赁,偏移區(qū)間0%-50%時(shí)

  • 矩形背景,高度放大40%梆暖,向上移動(dòng)30dp
  • 漸漸隱藏第1頁(yè)頂部伞访,底部圖;透明度漸變消失轰驳,偏移到50%時(shí)完全消失
private void transformRectBgFrom1To2Before(float positionOffset) {
        /** 矩形背景厚掷,高度放大40% */
        /**
         * 偏移到50%的時(shí)候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)));
        /** 矩形背景冒黑,向上移動(dòng)30dp */
        mRectBgCurrentTop = (int) (mPage1RectBgDefaultTop + (FIRST_TOP1 * positionOffset * (1 / FIRST_RATE)));
private void stepByHidePage1Views(float positionOffset) {
        /**
         * 偏移到50%的時(shí)候alpha需要為0田绑,view不可見(jiàn)
         *
         * 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頁(yè),頂部圖向下移動(dòng) */
        mPage1Top.currentTop = mPage1Top.defaultTop + (FIRST_TOP2 + FIRST_TOP1) * positionOffset * (1 / FIRST_RATE);

        /** 第1頁(yè)抡爹,底部圖跟隨頂部圖向下移動(dòng) */
        mPage1Bottom.currentTop = mPage1Top.currentTop + mPage1Top.defaultHeight + padding();
    }

第1頁(yè)->第2頁(yè)掩驱,偏移區(qū)間50%-100%時(shí)

  • 矩形背景,向下移動(dòng)60dp
  • 顯示第2頁(yè)裂變背景圖豁延,并左右平移
  • 逐漸顯示第2頁(yè)昙篙,頂部,底部圖诱咏,3張模特圖
private void transformRectBgFrom1To2After(float positionOffset) {
        /** 快速滑動(dòng)的時(shí)候苔可,可能丟失最后一次繪制,所以需要在這里調(diào)重新設(shè)置一次袋狞,保證變化完成 */
        mRectBgCurrentHeight = mPage2RectBgDefaultHeight;
        mRectBgCurrentTop = mPage1RectBgDefaultTop + FIRST_TOP1;
        /** 第1頁(yè)焚辅,在50%->100%區(qū)間偏移 */
        /** 矩形背景,在上上偏移30dp后苟鸯,向下偏移60dp */
        mRectBgCurrentTop =
            (int) (mPage1RectBgDefaultTop + FIRST_TOP1 + (FIRST_TOP2 * (positionOffset - FIRST_RATE) * 1.0 / (1 - FIRST_RATE)));
    }
private void stepByShowPage2Views(float positionOffset) {
        /** 第2頁(yè)同蜻,頂部圖,跟隨矩形背景下移 */
        mPage2Top.currentTop = mRectBgCurrentTop + padding();

        /** 第2頁(yè)早处,底部圖湾蔓,跟隨矩形背景下移 */
        mPage2Bottom.currentTop = mPage2Center[0].currentTop + mPage2Center[0].defaultHeight + padding();

        /** 第2頁(yè),計(jì)算裂變背景圖的偏移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%的時(shí)候alpha需要為0默责,偏移到100%,alpha需要為255咸包,不過(guò)此時(shí)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頁(yè)桃序,頂部,底部圖烂瘫,透明度漸變顯示媒熊,偏移量達(dá)到100%,完成顯示 */
        mPage2Top.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));
        mPage2Bottom.alpha((int) (255 * (positionOffset - FIRST_RATE) * (1 / (1 - FIRST_RATE))));

        /** 第2頁(yè)坟比,顯示中間3張模特圖 */
        for (int i = 0; i < mPage2Center.length; i++) {
            if (i == 0) {
                /** 第2頁(yè)芦鳍,顯示第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張顯示到一半時(shí)才顯示 */
                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張模特圖,需要根據(jù)第1張圖計(jì)算left */
                    mPage2Center[i].currentLeft =
                        mPage2Center[0].currentLeft + mPage2Center[0].currentWidth + padding();
                    mPage2Center[i].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding();
                    if (i == 2) {
                        /** 第3張模特圖注竿,根據(jù)第2張圖計(jì)算top */
                        mPage2Center[i].currentTop =
                            mPage2Center[1].currentTop + mPage2Center[1].currentHeight + padding();
                    }
                    mPage2Center[i].alpha((int) (255 * (positionOffset * rate * 2)));
                } else {
                    mPage2Center[i].alpha(0);
                }
            }
        }
    }

第2頁(yè)->第3頁(yè)茄茁,偏移區(qū)間0%-100%時(shí)

  • 矩形背景魂贬,寬度縮小15%
  • 矩形背景,上移20dp
private void transformRectBgFrom2To3(float positionOffset) {
        /** 快速滑動(dòng)的時(shí)候裙顽,可能丟失最后一次繪制付燥,所以需要在這里調(diào)重新設(shè)置一次,保證變化完成 */
        mRectBgCurrentHeight = mPage2RectBgDefaultHeight;
        mRectBgCurrentTop = mPage2RectBgDefaultTop;

        /** 矩形背景愈犹,寬度縮小15% */
        mRectBgCurrentWidth = (int) (mPage2RectBgDefaultWidth * (1 + SECOND_WIDTH * positionOffset));
        mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2;

        /** 矩形背景键科,上移20dp */
        mRectBgCurrentTop = (int) (mPage2RectBgDefaultTop + (SECOND_TOP * positionOffset));
    }

第2頁(yè)->第3頁(yè),偏移區(qū)間0%-50%時(shí)

  • 裂變背景圖跟隨滑動(dòng)漩怎,向左偏移至消失
  • 漸漸減少透明度勋颖,隱藏第2頁(yè)的頂部圖,3張模特圖勋锤,底部圖
private void stepByHidePage2Views(float positionOffset, int positionOffsetPixels) {
        /** 裂變背景圖饭玲,跟隨滑動(dòng),向左偏移至消失 */
        mPage2Split[0].currentLeft =
            (mPage2Split[0].defaultLeft - mPage1RectBgDefaultWidth - dp2px(15)) - positionOffsetPixels
                * (1 / SECOND_RATE);
        mPage2Split[1].currentLeft =
            (mPage2Split[1].defaultLeft + mPage1RectBgDefaultWidth + dp2px(15)) - positionOffsetPixels
                * (1 / SECOND_RATE);
        mPage2Split[0].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE))));
        mPage2Split[1].alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE))));

        /** 頂部圖叁执,3張模特圖茄厘,底部圖,跟隨矩形背景上移 */
        mPage2Top.currentTop = mRectBgCurrentTop + padding();
        mPage2Center[0].currentTop = mPage2Top.currentTop + mPage2Top.currentHeight + padding();
        mPage2Center[1].currentTop = mPage2Center[0].currentTop;
        mPage2Center[2].currentTop = mPage2Center[1].currentTop + mPage2Center[1].currentHeight;
        mPage2Bottom.currentTop = mPage2Center[0].currentTop + mPage2Center[0].currentHeight + padding();

        /** 漸漸減少透明度谈宛,隱藏第2頁(yè)的頂部圖次哈,3張模特圖,底部圖 */
        mPage2Top.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE))));
        mPage2Bottom.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE))));
        for (ViewModel viewModel : mPage2Center) {
            viewModel.alpha((int) (255 - (255 * positionOffset * (1 / SECOND_RATE))));
        }

        /** 因?yàn)榫匦伪尘白冋诉郝迹詽u漸減少第2頁(yè)頂部圖窑滞,底部圖的寬度,實(shí)現(xiàn)跟隨矩形背景寬度變化 */
        mPage2Top.currentWidth = mRectBgCurrentWidth - padding() * 2;
        mPage2Top.currentLeft = mRectBgCurrentLeft + padding();
        mPage2Bottom.currentWidth = mRectBgCurrentWidth - padding() * 2;
        mPage2Bottom.currentLeft = mRectBgCurrentLeft + padding();
        mPage2Bottom.currentLeft = mRectBgCurrentLeft + padding();

        /** 因?yàn)榫匦伪尘白冋嘶煮荩詽u漸減少第2葛假,3張模特圖的寬高,left和top滋恬,實(shí)現(xiàn)跟隨矩形背景寬度變化 */
        mPage2Center[0].currentLeft = mRectBgCurrentLeft + padding();
        mPage2Center[1].currentWidth = mRectBgCurrentWidth - padding() * 3 - mPage2Center[0].defaultWidth;
        mPage2Center[1].currentHeight = mPage2Center[1].currentWidth;
        mPage2Center[1].currentLeft = mPage2Center[0].currentLeft + mPage2Center[0].defaultWidth + padding();

        mPage2Center[2].currentWidth = mRectBgCurrentWidth - padding() * 3 - mPage2Center[0].defaultWidth;
        mPage2Center[2].currentHeight = mPage2Center[2].currentWidth;
        mPage2Center[2].currentLeft = mPage2Center[0].currentLeft + mPage2Center[0].defaultWidth + padding();
        mPage2Center[2].currentTop = mPage2Center[1].currentTop + mPage2Center[1].currentHeight + padding();
    }

第2頁(yè)->第3頁(yè),偏移區(qū)間50%-100%時(shí)

  • 依次顯示第3頁(yè)抱究,6張模特圖
private void stepByShowPage3Views(float positionOffset) {
        /** 第2頁(yè)恢氯,在50->100%區(qū)間偏移,顯示第3頁(yè)鼓寺,6張模特圖 */
        for (int i = 0; i < mPage3Model.length; i++) {
            if (i == 0) {
                /** 第1張模特圖先顯示 */
                if (mPage3Model[i].paint.getAlpha() < 255) {
                    mPage3Model[i].alpha((int) (255 * (positionOffset - SECOND_RATE) * (1 / (1 - SECOND_RATE))));
                }
            } else {
                /** 其他模特圖在前1張顯示50%透明度的時(shí)候再依次展示 */
                if (mPage3Model[i - 1].paint.getAlpha() >= 255 / 2) {
                    float rate = mPage3Model[i - 1].paint.getAlpha() / 255.0f - 0.5f;
                    mPage3Model[i].alpha((int) (255 * (rate * 2)));
                } else {
                    mPage3Model[i].alpha(0);
                }
            }

            /** 6張模特圖勋拟,跟隨矩形背景上移 */
            if (i < mPage3ModelResources.length / 2) {
                /** 第1排,3張模特圖的top計(jì)算 */
                mPage3Model[i].currentTop = mRectBgCurrentTop + padding();
            } else {
                /** 第1排妈候,3張模特圖的top敢靡,需要加上第一排的height */
                mPage3Model[i].currentTop = mRectBgCurrentTop + mPage3ModelDefaultHeight + padding() * 2;
            }
        }
    }

第3頁(yè)->第4頁(yè),偏移區(qū)間0%-100%時(shí)

  • 矩形背景苦银,寬度縮小10%
  • 矩形背景啸胧,高度縮小10%
  • 矩形背景赶站,逆時(shí)針旋轉(zhuǎn)10度
private void transformRectBgFrom3To4(float positionOffset) {
        /** 快速滑動(dòng)的時(shí)候,可能丟失最后一次繪制纺念,所以需要在這里調(diào)重新設(shè)置一次贝椿,保證變化完成 */
        mRectBgCurrentWidth = mPage3RectBgDefaultWidth;
        mRectBgCurrentTop = mPage3RectBgDefaultTop;

        /** 調(diào)整第4頁(yè),背景矩形的寬高和角度 */
        /** 背景矩形的寬度陷谱,在第3頁(yè)調(diào)整寬度的基礎(chǔ)上進(jìn)行縮小 */
        mRectBgCurrentWidth = mPage3RectBgDefaultWidth * (1 + THIRD_WIDTH * positionOffset);
        mRectBgCurrentLeft = getScreenWidth() / 2 - mRectBgCurrentWidth / 2;
        /** 背景矩形的高度烙博,在第2頁(yè)調(diào)整高度的基礎(chǔ)上進(jìn)行縮小 */
        mRectBgCurrentHeight = mPage3RectBgDefaultHeight * (1 + THIRD_HEIGHT * positionOffset);
        /** 背景矩形逆時(shí)針旋轉(zhuǎn) */
        mRectBgCurrentDegree = THIRD_DEGREE * positionOffset;
    }

第3頁(yè)->第4頁(yè),偏移區(qū)間0%-50%時(shí)

  • 漸漸縮放烟逊,隱藏6張模特圖
private void stepByHidePage3Views(float positionOffset) {
        /** 隱藏第3頁(yè)6張模特圖 */
        /** 從第1排渣窜,第3-1張開(kāi)始,依次縮放 */
        for (int i = mPage3ModelResources.length / 2 - 1; i >= 0; i--) {
            if (i == mPage3ModelResources.length / 2 - 1) {
                /** 如果是第1排宪躯,第3張乔宿,則開(kāi)始縮放 */
                mPage3Model[i].currentHeight =
                    mPage3Model[i].defaultHeight * (1 - positionOffset * (1 / (1 - THIRD_RATE)));
                mPage3Model[i].currentWidth =
                    mPage3Model[i].defaultWidth * (1 - positionOffset * (1 / (1 - THIRD_RATE)));
            } else {
                /** 如果是第1排,第1/2張眷唉,則判斷后1張縮放到一半的時(shí)候開(kāi)始自己的縮放 */
                if (mPage3Model[i + 1].currentHeight <= mPage3Model[i + 1].defaultHeight / 2) {
                    mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight * mPage3Model[i + 1].heightRate() * 2;
                    mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth * mPage3Model[i + 1].heightRate() * 2;
                } else {
                    mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight;
                    mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth;
                }
            }

            /** 跳轉(zhuǎn)left予颤,top,實(shí)現(xiàn)居中縮放 */
            mPage3Model[i].currentLeft =
                mPage3Model[i].defaultLeft + mPage3Model[i].defaultWidth / 2 - mPage3Model[i].currentWidth / 2;
            mPage3Model[i].currentTop =
                mPage3Model[i].defaultTop + mPage3Model[i].defaultHeight / 2 - mPage3Model[i].currentHeight / 2;
        }

        /** 從第1排冬阳,第4-6張開(kāi)始蛤虐,依次縮放 */
        for (int i = mPage3ModelResources.length / 2; i < mPage3ModelResources.length; i++) {
            if (i == mPage3ModelResources.length / 2) {
                /** 如果是第2排,第1張肝陪,則開(kāi)始縮放 */
                mPage3Model[i].currentHeight =
                    mPage3Model[i].defaultHeight * (1 - positionOffset * (1 / (1 - THIRD_RATE)));
                mPage3Model[i].currentWidth =
                    mPage3Model[i].defaultWidth * (1 - positionOffset * (1 / (1 - THIRD_RATE)));
            } else {
                /** 如果是第2排驳庭,第5/6張,則判斷前1張縮放到一半的時(shí)候開(kāi)始自己的縮放 */
                if (mPage3Model[i - 1].currentHeight <= mPage3Model[i - 1].defaultHeight / 2) {
                    mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight * mPage3Model[i - 1].heightRate() * 2;
                    mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth * mPage3Model[i - 1].heightRate() * 2;
                } else {
                    mPage3Model[i].currentHeight = mPage3Model[i].defaultHeight;
                    mPage3Model[i].currentWidth = mPage3Model[i].defaultWidth;
                }
            }

            /** 跳轉(zhuǎn)left氯窍,top饲常,實(shí)現(xiàn)居中縮放 */
            mPage3Model[i].currentLeft =
                mPage3Model[i].defaultLeft + mPage3Model[i].defaultWidth / 2 - mPage3Model[i].currentWidth / 2;
            mPage3Model[i].currentTop =
                mPage3Model[i].defaultTop + mPage3Model[i].defaultHeight / 2 - mPage3Model[i].currentHeight / 2;
        }
    }

第3頁(yè)->第4頁(yè),偏移區(qū)間50%-100%時(shí)

  • 漸漸顯示頂部圖狼讨,底部3張模特圖
  • 顯示第4頁(yè)裂變背景圖贝淤,并左右平移
private void stepByShowPage4Views(float positionOffset) {
        /** 顯示第4頁(yè),裂變背景圖政供,并向左右平移 */
        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);
            // 旋轉(zhuǎn)角度
            mPage4Split[i].matrix.postRotate(THIRD_DEGREE, currentLeft + mPage4RectBgDefaultWidth/2,
                    mPage4RectBgDefaultTop + mPage4RectBgDefaultHeight/2);

            mPage4Split[i].alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)))));
        }

        /** 顯示第4頁(yè)播聪,頂部模特圖 */
        mPage4Top.alpha((int) (255 * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)))));

        /** 顯示第4頁(yè),底部3張模特圖 */
        for (int i = 0; i < mPage4Model.length; i++) {
            if (i == 0) {
                mPage4Model[i].currentWidth =
                    mPage4Model[i].defaultWidth * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)));
                mPage4Model[i].currentHeight =
                    mPage4Model[i].defaultHeight * ((positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE)));
                mPage4Model[i].alpha((int) (255 * (positionOffset - THIRD_RATE) * (1 / (1 - THIRD_RATE))));
            } else {
                if (mPage4Model[i - 1].currentWidth >= mPage4ModelDefaultWidth / 2) {
                    mPage4Model[i].currentWidth =
                        mPage4Model[i].defaultWidth * ((mPage4Model[i - 1].widthRate() - 0.5f) * 2);
                    mPage4Model[i].currentHeight =
                        mPage4Model[i].defaultHeight * ((mPage4Model[i - 1].widthRate() - 0.5f) * 2);
                    mPage4Model[i].currentLeft =
                        mPage4Model[i - 1].currentLeft + mPage4Model[i - 1].currentWidth + padding();
                    mPage4Model[i].alpha((int) (255 * (mPage4Model[i - 1].widthRate() - 0.5f) * 2));
                }
            }
        }
    }

最后在onDraw()布隔,將計(jì)算好偏移值的view都繪制出來(lái)离陶。

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        /** 按重疊順序繪制 */
        if (fromPage1ToPage2(mCurrentPageIndex)) {

            /** 繪制第1頁(yè),底部背景圖 */
            drawBitmap(canvas, mPage1BottomBg);

            /** 繪制第2頁(yè)衅檀,裂變背景圖 */
            drawBitmap(canvas, mPage2Split[0]);
            drawBitmap(canvas, mPage2Split[1]);

            /** 繪制白色矩形背景 */
            drawWhiteRectBackgroud(canvas);

            drawPage1InCanvas(canvas);
            drawPage2InCanvas(canvas);

        } else if (fromPage2ToPage3(mCurrentPageIndex)) {

            /** 繪制第2頁(yè)招刨,裂變背景圖 */
            drawBitmap(canvas, mPage2Split[0]);
            drawBitmap(canvas, mPage2Split[1]);

            /** 繪制矩形背景 */
            drawWhiteRectBackgroud(canvas);

            drawPage2InCanvas(canvas);
            drawPage3InCanvas(canvas);

        } else if (fromPage3ToPage4(mCurrentPageIndex)) {
            /** 繪制第4頁(yè),裂變背景圖 */
            drawBitmapMatrix(canvas, mPage4Split[0]);
            drawBitmapMatrix(canvas, mPage4Split[1]);

            /** 繪制矩形背景 */
            drawWhiteRectBackgroud(canvas);

            drawPage3InCanvas(canvas);
            drawPage4InCanvas(canvas);

        } else if (isPage4(mCurrentPageIndex)) {

            /** 繪制第4頁(yè)哀军,裂變背景圖 */
            drawBitmapMatrix(canvas, mPage4Split[0]);
            drawBitmapMatrix(canvas, mPage4Split[1]);

            /** 繪制矩形背景 */
            drawWhiteRectBackgroud(canvas);

            drawPage4InCanvas(canvas);
        }
    }

最終效果:


高仿版本.gif
高仿版本.gif

目前還有一些細(xì)節(jié)的效果沉眶,以及適配打却,性能調(diào)優(yōu)還沒(méi)實(shí)現(xiàn)。雖然原理不難沦寂,不過(guò)要真正完整的實(shí)現(xiàn)以上效果学密,也算嘔心瀝血吧!難點(diǎn)就在于如何精細(xì)化的控制每個(gè)view的屬性传藏,因?yàn)轫?yè)面中每個(gè)圖片的位置腻暮,大小都是在參照其他view的基礎(chǔ)上進(jìn)行計(jì)算后得出的。現(xiàn)在市場(chǎng)上很多APP的歡迎頁(yè)都有類(lèi)似比較動(dòng)態(tài)的效果毯侦,原理就是ViewPager+Canvas繪制哭靖,掌握了本文的demo,其他實(shí)現(xiàn)原理應(yīng)該是一樣樣的侈离。感興趣的朋友可以Github上下載源碼查看试幽, 注釋還算清晰,有什么問(wèn)題頁(yè)歡迎提出卦碾,如果本文稍微對(duì)您有點(diǎn)啟示的話(huà)還請(qǐng)點(diǎn)個(gè)“喜歡”铺坞,謝謝了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市洲胖,隨后出現(xiàn)的幾起案子济榨,更是在濱河造成了極大的恐慌,老刑警劉巖绿映,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件擒滑,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡叉弦,警方通過(guò)查閱死者的電腦和手機(jī)丐一,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)淹冰,“玉大人库车,你說(shuō)我怎么就攤上這事∮K” “怎么了凝颇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)疹鳄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)芦岂,這世上最難降的妖魔是什么瘪弓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮禽最,結(jié)果婚禮上腺怯,老公的妹妹穿的比我還像新娘袱饭。我一直安慰自己,他們只是感情好呛占,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布虑乖。 她就那樣靜靜地躺著,像睡著了一般晾虑。 火紅的嫁衣襯著肌膚如雪疹味。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天帜篇,我揣著相機(jī)與錄音糙捺,去河邊找鬼。 笑死笙隙,一個(gè)胖子當(dāng)著我的面吹牛洪灯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竟痰,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼签钩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了坏快?” 一聲冷哼從身側(cè)響起铅檩,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎假消,沒(méi)想到半個(gè)月后柠并,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡富拗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年臼予,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了号胚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片端盆。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡霞掺,死狀恐怖坯汤,靈堂內(nèi)的尸體忽然破棺而出霎褐,到底是詐尸還是另有隱情掏秩,我是刑警寧澤拒担,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布淹办,位于F島的核電站追驴,受9級(jí)特大地震影響械哟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜殿雪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一暇咆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦爸业、人聲如沸其骄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拯爽。三九已至,卻和暖如春钧忽,著一層夾襖步出監(jiān)牢的瞬間毯炮,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工惰瓜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留否副,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓崎坊,卻偏偏與公主長(zhǎng)得像备禀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子奈揍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容