Android ViewPager.PageTransformer详解

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变大,示意图如下


有了以上说明,我们就可以利用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
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值