叨叨ViewPager那些事兒(二)

前言

上一篇叨叨ViewPager那些事兒(一)說了一點ViewPager概況,這篇打算說說實際應用。


從動畫效果說起

先祭上一份谷歌官方的Transformer示例
效果如下


代碼實現是這樣

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();

        if (position < -1) { // [-Infinity,-1)
            // This page is way off-screen to the left.
            view.setAlpha(0);

        } else if (position <= 0) { // [-1,0]
            // Use the default slide transition when moving to the left page
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]
            // Fade the page out.
            view.setAlpha(1 - position);

            // Counteract the default slide transition
            view.setTranslationX(pageWidth * -position);

            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}

乍一看有點懵懂,一步一問 慢慢來

transformPage(View view, float position)兩個參數是何含義


第一個參數爲各頁卡對象,第二個是各個頁卡對於當前所展示頁卡的相對位置,例如,當前頁卡的position爲0,下一張位置爲1,前一張則爲-1。而ViewPager滑動中,position的值是處於平滑變化中的,這就爲我們處理動畫提供了機會。

爲何position < -1position > 1時設置可見度爲0
如前問分析,position < -1position > 1指代頁卡所處區間爲[-Infinity,-1)和(1,+Infinity],在可見範圍之外,從節省繪製資源的角度出發,自然可見度爲0。

position (0,1]區間時爲何要設置偏移度爲(pageWidth * -position)
官方註釋Counteract the default slide transition,抵消頁面滑動時的默認偏移量。如頁面處在中間時偏移量爲0*width,移至右邊爲1*width,從中間往右移除時position0-->1漸變,默認偏移量爲position*width

滑動動畫的縮放效果如何計算
如效果圖示,當下一張頁卡相對位置從1-->0變化時,scale的變化方向爲MIN_SCALE(m)-->1,不妨列個公式
(1-position)/(position-0)=(m-scale)/(scale-1)s=(1-position)/(1-position*m)(對。。跟示例代碼不一樣。。但是效果一樣哦。。
也可以理解爲1-scale=|(m-1)|/(1-0)*position,scale從1-->m,位置從0-->1,即每單位position的變化量爲|(m-1)|/(1-0),乘上position得出變化量。

理論儲備到位之後,自力更生的第一步就是,實踐!
下邊我們來寫一個左右位移,同時展示三張卡片的動畫,預想中效果大概是這樣


根據設想計算,左右兩邊的頁卡大小應爲中間大頁卡的0.9倍,左右頁卡的偏移量約爲±0.2倍頁卡寬。
故設定MIN_Trans_INDEX = 0.2f,MIN_SCALE_INDEX = 0.9f。如上分析,我們只關心[-1,1]區間頁卡的動畫,
每單位position的scale變化量絕對值爲(1-MIN_SCALE_INDEX)/(1-0),即1-scale=|position|*(1-MIN_SCALE_INDEX)/(1-0)scale=1-|position|*(1-MIN_SCALE_INDEX)
同理,每單位position的translationx變化量爲pageWidth*(1-MIN_Trans_INDEX)/(1-0),即|0-translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX)|translationx|=|position|*pageWidth*(1-MIN_Trans_INDEX),然而viewpager頁卡滑動過程中的默認偏移量絕對值爲pageWidth * position,故需在上式基礎上減pageWidth * position,|translationx|=|position|*pageWidth*MIN_Trans_INDEX
計算完畢,寫入代碼後發現,效果是出來了,但是反應好像永遠。慢半拍。當前頁滑動到位後,下一頁纔出現,體驗不太美妙,如下動圖所示

查看代碼後思考,應該是[-1,1]位置限定導致,左右兩頁分別滑動到-1和1時纔開始做動畫,而此時這兩張頁卡已經出現在屏幕上,所以看起來就像“慢了半拍”。
嘗試增加一點“緩衝量”,改動如下

public class MyTransPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_Trans_INDEX = 0.2f;
    private static final float MIN_SCALE_INDEX = 0.9f;

    @Override
    public void transformPage(@NonNull View page, float position) {
        int pageWidth = page.getWidth();
        if (position < -1.1) { // [-Infinity,-1.1)
            // This page is way off-screen to the left.
            page.setAlpha(0);
        } else if (position <= 0) { // [-1.1,0]
            // Use the default slide transition when moving to the left page
            page.setAlpha(1);
            page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
            page.setScaleX((1 - MIN_SCALE_INDEX) * position + 1);
            page.setScaleY((1 - MIN_SCALE_INDEX) * position + 1);
            Log.i("test", page.getTag()+"--"+((1 - MIN_SCALE_INDEX) * position + 1));
        } else if (position <= 1.1) { // (0,1.1]
            // Fade the page out.
            page.setAlpha(1);
            // Counteract the default slide transition
            page.setTranslationX(-pageWidth * position * MIN_Trans_INDEX);
            page.setScaleX(1 - (1 - MIN_SCALE_INDEX) * position);
            page.setScaleY(1 - (1 - MIN_SCALE_INDEX) * position);
        } else { // (1.1,+Infinity]
            // This page is way off-screen to the right.
            page.setAlpha(0);
        }
    }
}

再次運行。這纔是想要的效果嘛


問題來了

之前遇到過一個現象,刷新ViewPager,調用notifyDataSetChanged(),如下。

誒,怎麼肥事!左右兩邊的頁卡去哪了!冷靜一下,刷新時對當前頁卡重繪,若不需滾動,則pageoffset始終爲0,transformer的動畫效果無法顯示。看來要想平穩刷新,還需手動更新單條數據。
行動起來,更改如下邏輯

@Override
    public void onClick(View v) {
        if (v.getId() == R.id.tv_notify) {
            if (mCurPageIndex == 3) {//測試,代表邏輯需跳轉到的頁卡
                isNeedNotify = true;
            }
            //更新當前頁卡數據
            updateViewPager(mCurPageIndex);
        }
    }
private void updateViewPager(int position) {
        if (isNeedNotify) {//需要滾動時,調用notifyDataSetChanged重走instantiateItem和destroyItem邏輯
            mPagerAdapter.notifyDataSetChanged();
            mViewPager.setCurrentItem(3);
            isNeedNotify = false;
        }
        //僅需更新當前頁時則單獨刷新當前頁卡view
        MyViewPagerItem item = mPageItemList.get(position);
        if (item != null) {
            PageItemBean bean = new PageItemBean();
            bean.num = position;
            bean.tip = mContext.getString(R.string.num_tip, position + 1 + "");
            item.updateUI(bean);
        }
    }

重跑程序,問題解決。
爲解決問題臨時想的方案,是實現更新的一條思路但感覺不夠優,歡迎各路大神指點。


最後

本想多寫一點,但動畫已經佔了不少篇幅,爲避免超長文先在這結束吧。這個系列會努力補充,歡迎多多指點和關注。

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