做為我viewpager的第一篇,我覺得說一說這個頁面變換還是很對頭的,這個頁面變換可是我們精通viewpager必會的第一項
何為頁面切換效果,就是我們滑動viewpager時當(dāng)前頁和下一頁以什么樣式呈現(xiàn),廢話不多說,先來看下效果圖:
實現(xiàn)原理:
大家看著像是用動畫實現(xiàn)的,其實很簡單,google已經(jīng)給我們做好準(zhǔn)備了,實現(xiàn) ViewPager.PageTransformer 這個接口,然后把這個實現(xiàn)對象設(shè)置到viewpager里即可。
// 這是實現(xiàn)頁面切換接口的實現(xiàn)類類
public class AlpheScaleTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
....
}
}
// 然后把這個實現(xiàn)對象設(shè)置到viewpager里
mViewPager.setPageTransformer(true, new AlpheScaleTransformer());
看著是不是很簡單啊,其實細(xì)細(xì)想來,這和實現(xiàn)動畫是一個原理。何為動畫,快速切換的靜態(tài)圖片罷了,在我們切換viewpager頁面時,頁面隨著手指滾動,滾動一次,viewpager就會調(diào)一次 ViewPager.PageTransformer 這個接口實現(xiàn)類來重置當(dāng)前頁和下一頁的樣式,在這里我們做縮放,透明度等各種變換來操作view的各種屬性,然后隨著系統(tǒng)16ms刷新一幀,也就成了我們看到的樣子。google已經(jīng)提供了這個接口,而且還放出了官方的demo,所以大家不要怕,這個切換的知識點很簡單的,所以我再viewpager的第一篇才來說這塊。
google官方demo:
package com.example.zb.bloodcrownviewpagertransformer.transformer;
import android.support.v4.view.ViewPager;
import android.view.View;
public class GooglePageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
page.setAlpha(1);
page.setTranslationX(0);
page.setScaleX(1);
page.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
page.setAlpha(1 - position);
// Counteract the default slide transition
page.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
}
我的實現(xiàn):
public class AlpheScaleTransformer implements ViewPager.PageTransformer {
private float minAlphe = 0.3f;
private float minScale = 0.7f;
@Override
public void transformPage(View page, float position) {
if (position < -1 || position > 1) {
page.setAlpha(1);
page.setTranslationX(0);
page.setScaleX(1);
page.setScaleY(1);
return;
}
if (position >= -1 && position <= 0) {
return;
}
if (position > 0 && position <= 1) {
page.setAlpha(minAlphe + (1 - minAlphe) * (1 - Math.abs(position)));
page.setScaleX(minScale + (1 - minScale) * (1 - Math.abs(position)));
page.setScaleY(minScale + (1 - minScale) * (1 - Math.abs(position)));
page.setPivotX(page.getWidth() / 2);
page.setPivotY(page.getHeight() / 2);
page.setTranslationX(page.getWidth() * -position);
}
}
}
實現(xiàn)要點
目標(biāo)接口:
public class AlpheScaleTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
....
}
}
上面的熱鬧看完了,那么我們來具體的說下實現(xiàn)的要點:
- 接口參數(shù)介紹
- 接口被誰調(diào)用,調(diào)用次數(shù)
- 結(jié)合實際理解接口參數(shù)
接口參數(shù)介紹
ViewPager.PageTransformer 接口里面只有一個方法,用來不停刷新頁面樣式的。那么我們觀看里面的 transformPage 這個方法,參數(shù)是一個view和一個數(shù)值。view自然是viewpager的每個頁面了,position則是當(dāng)前這個view頁面所處的位置。
接口被誰調(diào)用,調(diào)用次數(shù)
那么大伙想一想我們滑動viewpager時是幾個頁面在動?咱就說一般的viewpager,那么發(fā)生變化的頁面一個是當(dāng)前頁,一個是下一頁或是上一頁啦,那么算下來就是2個頁面,就是2個view啦。這么說的話2個頁面都是同時再運動,那么 transformPage(View page, float position) 這個方法必然也是會被調(diào)用多次了。會調(diào)用幾次,被誰調(diào)用?也許大家會猜測是參與運動變化的這2個頁面。經(jīng)過測試發(fā)現(xiàn)不僅僅是這2個頁面會調(diào)用這個接口方法,而是viewpager適配器中所有緩存的view都會調(diào)用這個接口方法,大家想過沒有,為什么會是這樣,因為viewpager不知道緩存的view是不是正在顯示,所以干脆緩存的所有view都去做頁面變換,萬一都在顯示呢,舉個例子,大家都看過同時顯示3個view的viewpager吧
結(jié)合實際理解接口參數(shù)
transformPage(View page, float position) 里面就2個參數(shù),view大家都明白了吧,每個緩存的view都會跑一次這個方法。position比較麻煩也是這個方法的核心,所以要重點細(xì)說。上面說position是描述頁面所處的位置,在開始變換前,中間的頁面(也是當(dāng)前頁面)position是0,左邊的頁面position是-1,右邊的頁面position是1。記住中間頁面position永遠(yuǎn)是0,整數(shù)表示當(dāng)前頁右邊的頁,負(fù)數(shù)表示當(dāng)前頁左邊的頁
舉個例子,當(dāng)前頁是第二頁,我們左滑,實際是第二頁往左運動到手機盡頭不顯示,第三頁從右邊慢慢進(jìn)入到手機屏幕代替第二頁的位置。postion的變化:
- 第二頁(原當(dāng)前頁) 0 ~ -1
- 第三頁1 ~ 0
第二頁從當(dāng)前頁運動到屏幕左側(cè),所以是 從0 ~ -1 的變化。
第三頁因為是在第二頁的右邊,所以是 從1 ~ 0的變化。
大家想想是不是這樣,整數(shù)表示右邊,第三頁的確是在第二頁的右邊,所以第三頁開始position是1 ,中間的當(dāng)前頁因為是position0,所以第三頁運動到屏幕中間代碼第二頁時,position就是0啦。第二頁呢因為是中間頁,開始position是0,第二頁向左運動,因為第二頁在第三頁的左邊,所以運動結(jié)束時是-1。
大家來看張position變換的表:(這張圖是我扒來的)
看上面這張圖,基本說明了position的數(shù)值范圍,position都是以1做為整數(shù)變更的,0是中間頁面,-1是左邊的頁面,那么-2就是左邊的左邊,正數(shù)同理。一般viewpager的頁面切換至涉及到2個頁面,取值 < -1的,> 1的一般都是不可見的頁面,負(fù)數(shù)都是表示左邊的頁面或是往左邊一棟的頁面,正數(shù)表示右邊的頁面或是往右邊一棟的頁面,這就是viewpager頁面切換的核心,參透position參數(shù)的變化,說道這里大伙看看上面google的官方實現(xiàn)或是我的現(xiàn)實都可以,我的實現(xiàn)里position < -1 的我都沒有操作,所以在效果圖里,左邊的頁面除了默認(rèn)的位移什么效果都沒有,變化都是來自于右邊就是因為position的取舍。默認(rèn)都是會帶上位移的,我們給view一個反向的位移就可以讓view實現(xiàn)在原地變換的效果了。
if (position > 0 && position <= 1) {
page.setAlpha(minAlphe + (1 - minAlphe) * (1 - Math.abs(position)));
page.setScaleX(minScale + (1 - minScale) * (1 - Math.abs(position)));
page.setScaleY(minScale + (1 - minScale) * (1 - Math.abs(position)));
page.setPivotX(page.getWidth() / 2);
page.setPivotY(page.getHeight() / 2);
page.setTranslationX(page.getWidth() * -position); // 這行就是加一個反響的位移
}
總結(jié)
上面我就是說了下position的數(shù)值變化,其他的沒怎么說,各位看官要是理解的不是很透的話請看參考資料,尤其是參考資料里 transformer 變換的庫(ViewPagerTransforms),transformer變化的核心都在這些transformer的實現(xiàn)類上面了。
另外我們在自己計算view屬性變換的公式時,只要計算好一個方向的公式就好了,反方向postion的數(shù)值會走反向變化。
參考資料
- ViewPager PageTransformer探索
- Android 實現(xiàn)個性的ViewPager切換動畫 實戰(zhàn)PageTransformer(兼容Android3.0以下)
- Android 自定義 ViewPager 打造千變?nèi)f化的圖片切換效果