Android ViewPager.PageTransformer詳解

PageTransformer是ViewPager內部定義的接口,實現(xiàn)該接口并應用于ViewPager可以控制ViewPager中item view的滑動效果。先上一張示例圖


接下來我們看一下PageTransformer源碼及api介紹,比較簡單

    /**
     * A PageTransformer is invoked whenever a visible/attached page is scrolled.
     * This offers an opportunity for the application to apply a custom transformation
     * to the page views using animation properties.
     *
     * <p>As property animation is only supported as of Android 3.0 and forward,
     * setting a PageTransformer on a ViewPager on earlier platform versions will
     * be ignored.</p>
     */
    public interface PageTransformer {
        /**
         * Apply a property transformation to the given page.
         *
         * @param page Apply the transformation to this page
         * @param position Position of page relative to the current front-and-center
         *                 position of the pager. 0 is front and center. 1 is one full
         *                 page position to the right, and -1 is one page position to the left.
         */
        void transformPage(@NonNull View page, float position);
    }

簡單看一下接口描述,直白的翻譯過來,大致意思是:當附著于ViewPager中的頁面滑動時,會觸發(fā)PageTransformer 實例(transformPage方法)。PageTransformer 支持用戶通過動畫屬性自定義頁面滑動效果。

PageTransformer只有一個方法: transformPage(@NonNull View page, float position)。該方法的api描述,我想多數(shù)人都沒有搞清楚,尤其是對position參數(shù)理解,下面就著重講解一下該方法及參數(shù)。
在講解參數(shù)前,我們先定義兩個描述語:基準參考點、page頁面間距歸一化

基準參考點
基準參考點,是指ViewPager處于(最近一次處于)SCROLL_STATE_IDLE狀態(tài)(此狀態(tài)下cureent page完整顯示,沒有滑動偏移。備注:若處于滑動過程,則取最近一次處于SCROLL_STATE_IDLE狀態(tài))時cureent page的position值,為0。 transformPage方法中的position都是相對于這個基準參考點的相對值。以基準參考點為中心,建立一維坐標,左側為負,右側為正,來描述page的position值。

page頁面寬度歸一化
這個歸一化是指,將page物理寬度歸一化為1,以此為基礎進行page相對于基準參考點的position值計算

根據(jù)以上兩個描述語的定義,顯然相鄰page間距是1。下面貼上SCROLL_STATE_IDLE狀態(tài)下示意圖:


接下來講解參數(shù):
參數(shù)page是ViewPager持有的頁面(包括cureent page)的rootView,position是page相對于基準參考點的偏移量,滑動過程中可標識page的偏移程度,周期為1。根據(jù)基準參考點及page頁面寬度歸一化的描述,在SCROLL_STATE_IDLE狀態(tài)下,current page的position為0,上一頁的position為-1.0,下一頁的position為1.0,依此類推。position隨ViewaPger滑動趨勢發(fā)生相應變化:
向左滑動時,page相對于基準參考點向左偏移,position減小;向右滑動時page相對于基準參考點向右偏移,position變大,其絕對值反應了page與基準參考點間的距離。滑動示意圖如下


有了以上說明,我們就可以利用transformPage(@NonNull View page, float position)方法操控page滑動效果了。根據(jù)page參數(shù),可以拿到page的真實物理寬度與高度,根據(jù)position計算動效的參數(shù)值。下面貼出具有層疊效果的PageTransformer實例代碼:

public class HorizontalStackTransformerWithRotation implements ViewPager.PageTransformer {
    private static final float CENTER_PAGE_SCALE = 0.8f;
    private int offscreenPageLimit;
    private ViewPager boundViewPager;

    public HorizontalStackTransformerWithRotation(@NonNull ViewPager boundViewPager) {
        this.boundViewPager = boundViewPager;
        this.offscreenPageLimit = boundViewPager.getOffscreenPageLimit();
    }

    @Override
    public void transformPage(@NonNull View view, float position) {
        int pagerWidth = boundViewPager.getWidth();
        float horizontalOffsetBase = (pagerWidth - pagerWidth * CENTER_PAGE_SCALE) / 2 / offscreenPageLimit + DisplayUtil.dp2px(15);

        if (position >= offscreenPageLimit || position <= -1) {
            view.setVisibility(View.GONE);
        } else {
            view.setVisibility(View.VISIBLE);
        }

        if (position >= 0) {
            float translationX = (horizontalOffsetBase - view.getWidth()) * position;
            view.setTranslationX(translationX);
        }
        if (position > -1 && position < 0) {
            float rotation = position * 30;
            view.setRotation(rotation);
            view.setAlpha((position * position * position + 1));
        } else if (position > offscreenPageLimit - 1) {
            view.setAlpha((float) (1 - position + Math.floor(position)));
        } else {
            view.setRotation(0);
            view.setAlpha(1);
        }
        if (position == 0) {
            view.setScaleX(CENTER_PAGE_SCALE);
            view.setScaleY(CENTER_PAGE_SCALE);
        } else {
            float scaleFactor = Math.min(CENTER_PAGE_SCALE - position * 0.1f, CENTER_PAGE_SCALE);
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
        }

        // test code: view初始化時,設置了tag
        String tag = (String) view.getTag();
//        LogUtil.e("viewTag" + tag, "viewTag: " + (String) view.getTag() + " --- transformerPosition: " + position + " --- floor: " + Math.floor(position) + " --- childCount: "+ boundViewPager.getChildCount());
        ViewCompat.setElevation(view, (offscreenPageLimit - position) * 5);
    }
}

為了加深理解,您可以在初始化item view的時候,為其設置一個tag,tag值可設為item在列表中的index,并輸出日志觀察transformPage(View view, float position)方法中position變化情況
完整示例:https://github.com/670832188/TestApp

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容