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
网友评论