標籤:
就算Google從很早開始就內建了設定引導頁動畫的介面,但是就我目前看來市面上使用引導頁動畫的還是很少的,也不知道是為什麼,一想到Material Design的使用率也這麼少表示很心塞。
首先來看看市面上千篇一律的引導頁效果,諾:
很單調對不對,你們沒看吐我都看吐了,再看一份加了引導頁動畫效果的:
有沒有瞬間耳目一新的感覺,下面就談談如何做出這樣的引導頁動畫。
其實從Android 3.0也就是API 11開始Android就內建了一個PageTransformer介面用來實現ViewPager動畫效果並為之加入了setPageTransformer方法來自訂我們自己的動畫效果,用的時候很簡單:
viewpager.setPageTransformer(false, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { // do transformation here }});
這裡setPageTransformer傳入了兩個參數,第一個布爾型參數表示的意思就是在兩個頁面切換產生動畫效果時候是否要反轉一下讓下一個頁面在上一個頁面底下,因為ViewPager預設下一個頁面是繪製在上一個頁面的上面,這裡一般傳入true,第二次參數才是重點,這裡實現了PageTransformer介面,然後我們所有需要的動畫效果都在transformPage這個介面方法裡面實現,現在我們來看看這個方法。
我們發現transformPage方法也有兩個參數,第一個參數就表示當前顯示在螢幕上的Activity或者Fragment,這個不管,後一個參數是重點,這個position並不是我們引導頁頁面的position,下面借用一張圖來說明這個position參數:
圖片出處
Google官方對這個參數的解釋是,這個參數表明了一個給定的頁面相對於螢幕中心的位置,並且這個參數隨著我們的滑動會動態變化,最重要的一點是這個position參數是相對於我們的螢幕左邊緣來說的,如果當前的頁面剛好佔滿了整個螢幕,就如所示的1介面,那麼這個頁面的position參數就是0,如果一個頁面剛從螢幕右邊緣划出來,可以理解為中頁面1和頁面2的交界線剛冒頭的情況,那麼這個頁面的position值就是1,如果某個時刻我們把頁面1向左滑動一半,導致螢幕中央既有一半頁面1顯示又有一半頁面2顯示的時候,這時候頁面1的position值就是-0.5,頁面2的position值就是0.5,都是相對於左邊緣來說的,而且左邊緣的值固定是0,就是下面這種情況:
那麼知道這個值有什麼用呢,其實多虧了這個值我們才能做出更好的動畫效果,要知道這個值是動態變化的,有了一個動態變化的值就可以做出動態變化的效果,我們可以看看Google是怎麼用它的:
public class ZoomOutPageTransformer implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.85f; private static final float MIN_ALPHA = 0.5f; public void transformPage(View view, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-1,1] // Modify the default slide transition to shrink the page as well float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); float vertMargin = pageHeight * (1 - scaleFactor) / 2; float horzMargin = pageWidth * (1 - scaleFactor) / 2; if (position < 0) { view.setTranslationX(horzMargin - vertMargin / 2); } else { view.setTranslationX(-horzMargin + vertMargin / 2); } // Scale the page down (between MIN_SCALE and 1) view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); // Fade the page relative to its size. view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } }}
這份代碼的實際效果如下:
可以看到很明顯的縮放以及透明度的變換,我們看看代碼是怎麼利用這個position參數的:
if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0);
首先這裡判斷如果position參數是負無窮到-1的時候,也就是此時這個page划出了螢幕左邊緣之外不可見得情況,直接設定為透明的
} else if (position <= 1) { // [-1,1] // Modify the default slide transition to shrink the page as well float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); float vertMargin = pageHeight * (1 - scaleFactor) / 2; float horzMargin = pageWidth * (1 - scaleFactor) / 2; if (position < 0) { view.setTranslationX(horzMargin - vertMargin / 2);
接下裡判斷如果這個頁面的position處於-1到1之間,也就是最極端的情況是這個頁面即將向左邊滑出螢幕之外,或者說這個頁面還在螢幕右邊緣即將滑入我們的視線,這時就用到了我們的position參數,這裡Google把它當成了一個縮放係數來用;
float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
這裡1 - Math.abs(position)) 的值在0~1之間,並且代碼判斷了MIN_SCALE和1 - Math.abs(position)) 的大小並取其中的最大值,MIN_SCALE是最上面定義的最小縮放係數,可以看到這一行代碼得出來的縮放係數是處於MIN_SCALE和1之間的,然後下面的代碼就是根據position位置參數的變化來進行page的水平移動效果,其實ViewPager提供了相當多的方法共給我們去操作,諸如:setScale設定縮放啦,setRotation設定旋轉效果,還有這裡的setTranslationX設定水平移動效果等等,結合position這個動態變化的參數可以做出很多意想不到的動畫,比如下面這個旋轉動畫:
代碼如下:
public class RotateDownPageTransformer implements ViewPager.PageTransformer{ private static final float ROT_MAX = 20.0f; private float mRot; public void transformPage(View view, float position) { Log.e("TAG", view + " , " + position + ""); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. ViewHelper.setRotation(view, 0); } else if (position <= 1) // a頁滑動至b頁 ; a頁從 0.0 ~ -1 ;b頁從1 ~ 0.0 { // [-1,1] // Modify the default slide transition to shrink the page as well if (position < 0) { mRot = (ROT_MAX * position); ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(view, view.getMeasuredHeight()); ViewHelper.setRotation(view, mRot); } else { mRot = (ROT_MAX * position); ViewHelper.setPivotX(view, view.getMeasuredWidth() * 0.5f); ViewHelper.setPivotY(view, view.getMeasuredHeight()); ViewHelper.setRotation(view, mRot); } // Scale the page down (between MIN_SCALE and 1) // Fade the page relative to its size. } else { // (1,+Infinity] // This page is way off-screen to the right. ViewHelper.setRotation(view, 0); } }}
注意:這裡的ViewHelper類需要引用nineoldandroids第三方動畫庫來實現,具體的東西我都打包好了在最下面有下載連結,需要的自行取用。
更酷炫的效果
其實除了對position這個參數進行處理,我們還可以對view進行動畫處理,前面提到transformPage這個介面方法第一個參數view就是當前可見的Activity或Fragment,那麼我們還可以對這個Activity或Fragment內部的元素進行處理來達到視察動畫的效果,一個很好地例子:
public void transformPage(View view, float position) { int pageWidth = view.getWidth(); if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-1,1] mBlur.setTranslationX((float) (-(1 - position) * 0.5 * pageWidth)); mBlurLabel.setTranslationX((float) (-(1 - position) * 0.5 * pageWidth)); mDim.setTranslationX((float) (-(1 - position) * pageWidth)); mDimLabel.setTranslationX((float) (-(1 - position) * pageWidth)); mCheck.setTranslationX((float) (-(1 - position) * 1.5 * pageWidth)); mDoneButton.setTranslationX((float) (-(1 - position) * 1.7 * pageWidth)); // The 0.5, 1.5, 1.7 values you see here are what makes the view move in a different speed. // The bigger the number, the faster the view will translate. // The result float is preceded by a minus because the views travel in the opposite direction of the movement. mFirstColor.setTranslationX((position) * (pageWidth / 4)); mSecondColor.setTranslationX((position) * (pageWidth / 1)); mTint.setTranslationX((position) * (pageWidth / 2)); mDesaturate.setTranslationX((position) * (pageWidth / 1)); // This is another way to do it } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); }}
工程地址
最後,附上整個項目的地址:項目地址
有任何問題可以留言探討
使用ViewPager動畫來做出不一樣的引導頁