Android ViewPager.PageTransforme

作者: 乱世白衣 | 来源:发表于2018-04-25 12:05 被阅读230次

    PageTransformer是ViewPager内部定义的接口,实现该接口并应用于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中的页面滑动时,会触发PageTransformer 实例(transformPage方法)。PageTransformer 支持用户通过动画属性自定义页面滑动效果。

    PageTransformer只有一个方法: transformPage(@NonNull View page, float position)。该方法的api描述,我想多数人都没有搞清楚,尤其是对position参数理解,下面就着重讲解一下该方法及参数。
    在讲解参数前,我们先定义两个描述语:基准参考点、page页面间距归一化

    基准参考点
    基准参考点,是指ViewPager处于(最近一次处于)SCROLL_STATE_IDLE状态(此状态下cureent page完整显示,没有滑动偏移。备注:若处于滑动过程,则取最近一次处于SCROLL_STATE_IDLE状态)时cureent page的position值,为0。 transformPage方法中的position都是相对于这个基准参考点的相对值。以基准参考点为中心,建立一维坐标,左侧为负,右侧为正,来描述page的position值。

    page页面宽度归一化
    这个归一化是指,将page物理宽度归一化为1,以此为基础进行page相对于基准参考点的position值计算

    根据以上两个描述语的定义,显然相邻page间距是1。下面贴上SCROLL_STATE_IDLE状态下示意图:


    接下来讲解参数:
    参数page是ViewPager持有的页面(包括cureent page)的rootView,position是page相对于基准参考点的偏移量,滑动过程中可标识page的偏移程度,周期为1。根据基准参考点及page页面宽度归一化的描述,在SCROLL_STATE_IDLE状态下,current page的position为0,上一页的position为-1.0,下一页的position为1.0,依此类推。position随ViewaPger滑动趋势发生相应变化:
    向左滑动时,page相对于基准参考点向左偏移,position减小;向右滑动时page相对于基准参考点向右偏移,position变大,其绝对值反应了page与基准参考点间的距离。滑动示意图如下


    有了以上说明,我们就可以利用transformPage(@NonNull View page, float position)方法操控page滑动效果了。根据page参数,可以拿到page的真实物理宽度与高度,根据position计算动效的参数值。下面贴出具有层叠效果的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

    相关文章

      网友评论

        本文标题:Android ViewPager.PageTransforme

        本文链接:https://www.haomeiwen.com/subject/cyndlftx.html