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