android ViewPager动画的实现原理及效果
轉(zhuǎn)自:http://www.lightskystreet.com/2014/12/15/viewpager-anim/
前言
前兩天看到鮑永章分享的Great animations with PageTransformer以及農(nóng)民伯伯分享的Create ViewPager transitions?文章,都是通過(guò)ViewPager來(lái)實(shí)現(xiàn)酷炫的動(dòng)畫(huà),而現(xiàn)在的App中ViewPager的動(dòng)畫(huà)使用也非常的廣泛。正好最近一直研究動(dòng)畫(huà),那么就趁熱打鐵,分析一下相關(guān)的開(kāi)源庫(kù)吧。
本篇文章介紹的ViewPager動(dòng)畫(huà),可以分為兩類(lèi),第一類(lèi)是針對(duì)于ViewPager的界面滑動(dòng)動(dòng)畫(huà)(這個(gè)是PageTransformer的真正用途),分析并比較了AndroidImageSlider和JazzyViewPager兩種實(shí)現(xiàn),第二類(lèi)是對(duì)ViewPager中的內(nèi)容進(jìn)行動(dòng)畫(huà)處理,這個(gè)是這個(gè)是PageTransformer的巧妙應(yīng)用,處理好了可以達(dá)到很棒的交互效果,示例是Yahoo天氣的視差效果。
ViewPager動(dòng)畫(huà)的實(shí)現(xiàn)原理
從3.0開(kāi)始,ViewPager開(kāi)始支持自定義切換動(dòng)畫(huà),暴露的接口為PageTransformer,因此只要實(shí)現(xiàn)PageTransformer接口和其唯一的方法transformPage(View view, float position)即可。
| 1234567891011121314151617181920 | /*** 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.*/public void transformPage(View page, float position);} |
- 參數(shù)position
給定界面的位置相對(duì)于屏幕中心的偏移量。在用戶(hù)滑動(dòng)界面的時(shí)候,是動(dòng)態(tài)變化的。那么我們可以將position的值應(yīng)用于setAlpha(), setTranslationX(), or setScaleY()方法,從而實(shí)現(xiàn)自定義的動(dòng)畫(huà)效果。
另外在ViewPager滑動(dòng)時(shí),內(nèi)存中存活的Page都會(huì)執(zhí)行transformPage方法,在滑動(dòng)過(guò)程中涉及到兩個(gè)Page,當(dāng)前頁(yè)和下一頁(yè),而它們的position值是相反的(因?yàn)槭窍鄬?duì)運(yùn)動(dòng),一個(gè)滑入一個(gè)滑出),比如,頁(yè)面A向右滑動(dòng)到屏幕一半,頁(yè)面B也正好處于一半的位置,那么A和B的position為:0.5 和 -0.5
position == 0 :當(dāng)前界面位于屏幕中心的時(shí)候
position == 1 :當(dāng)前Page剛好滑出屏幕右側(cè)
position == -1 :當(dāng)前Page剛好滑出屏幕左側(cè)
AndroidImageSlider動(dòng)畫(huà)庫(kù)解析
要說(shuō)到動(dòng)畫(huà)庫(kù),肯定會(huì)想到代碼家,沒(méi)錯(cuò),代碼家也開(kāi)源了一個(gè)ViewPager效果的庫(kù):AndroidImageSlider?,我們就來(lái)分析下這個(gè)庫(kù)的實(shí)現(xiàn)。AndroidImageSlider除了基本的page動(dòng)畫(huà)外,也支持用戶(hù)為Page內(nèi)容添加自定義的動(dòng)畫(huà),比如下面描述框的動(dòng)畫(huà)。
AndroidImageSlider兼容性的實(shí)現(xiàn)原理:
因?yàn)锳PI 11才開(kāi)始支持PagerTransformer. 這里面修改了Android系統(tǒng)的ViewPager,名為ViewPagerEx,ViewPager里面有一段if邏輯判斷是否在3.0以上使用PagerTransformer。因?yàn)镻agerTransformer動(dòng)畫(huà)效果的實(shí)現(xiàn)依賴(lài)了PropertyViewAnim。但代碼家的這個(gè)庫(kù)使用NineOldAndroids實(shí)現(xiàn)了向3.0之前的兼容。因此就把這個(gè)if條件去掉了,其它部分都沒(méi)變。
| 123456 | /*** @author daimajia : I just remove the if condition in setPageTransformer() to make it compatiable with Android 2.0+* of course, with the help of the NineOldDroid.* Thanks to JakeWharton.* http://github.com/JakeWharton/NineOldAndroids*/ |
AndroidImageSlider的總體設(shè)計(jì)
-
BaseTransformer
所有Transformer的基類(lèi),實(shí)現(xiàn)了ViewPagerEx.PageTransformer接口以及transformPage方法,并提供了onPreTransform(View view, float position)、onPostTransform(View view, float position)、onTransform(View view, float position);
分別在transformPage前后調(diào)用,用來(lái)處理為每一次的執(zhí)行動(dòng)畫(huà)前的準(zhǔn)備和結(jié)束動(dòng)作,比如還原所有的動(dòng)畫(huà)狀態(tài)。 -
BaseAnimationInterface:
ViewPagerEx執(zhí)行Transformer動(dòng)畫(huà)的時(shí)候注入一些自己的動(dòng)畫(huà)。你需要實(shí)現(xiàn)該接口,然后實(shí)現(xiàn)以下4個(gè)方法,獲取SlideView中的View,實(shí)現(xiàn)自己的動(dòng)畫(huà)。比如底部的DescriptionText動(dòng)畫(huà)DescriptionAnimation?出現(xiàn)時(shí)的動(dòng)畫(huà)就是onNextItemAppear中添加的,你可以點(diǎn)入,看下源碼
onPrepareCurrentItemLeaveScreen(View current)
onPrepareNextItemShowInScreen(View next)
onCurrentItemDisappear(View view)
onNextItemAppear(View view)
而這4個(gè)方法的調(diào)用是在BaseTransformer中。BaseTransformer統(tǒng)一管理了Page滑動(dòng)時(shí)的所有動(dòng)畫(huà)。為了獲取這4個(gè)方法的調(diào)用時(shí)機(jī),也是煞費(fèi)苦心啊,先說(shuō)下思路:
因?yàn)閂iewPager滑動(dòng)的時(shí)候transformPage方法是實(shí)時(shí)調(diào)用的,這里獲取最初兩次調(diào)用時(shí)傳入的position進(jìn)行比較。通過(guò)一個(gè)HashMap<view, arraylist>來(lái)維護(hù)不同pageView的多個(gè)position。但這里為了優(yōu)化,只取前兩個(gè)position做比較。
先判斷起始滑動(dòng)的方向,然后再判斷下一次滑動(dòng)的方向,兩次結(jié)果作差來(lái)判斷到底哪個(gè)page要離開(kāi)界面還是進(jìn)入界面。
確定2個(gè)界面的4種臨界狀態(tài):
| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 | if(mCustomAnimationInterface != null){ if(h.containsKey(view) == false || h.get(view).size() == 1){ if(position > -1 && position < 1){ if(h.get(view) == null){h.put(view,new ArrayList<Float>());//為每個(gè)View創(chuàng)建一個(gè)List,來(lái)存儲(chǔ)偏移分?jǐn)?shù)position}h.get(view).add(position);//向指定的View中添加偏移分?jǐn)?shù) if(h.get(view).size() == 2){ float zero = h.get(view).get(0); float cha = h.get(view).get(1) - h.get(view).get(0);//當(dāng)前滑動(dòng)的位移偏移量分?jǐn)?shù)差: newfraction - oldfraction if(zero > 0){//起始時(shí),向左滑動(dòng),當(dāng)前Page中心位于屏幕中心左側(cè) //判斷滑動(dòng)趨勢(shì),如果繼續(xù)向左側(cè)滑動(dòng),position從0到1越來(lái)越大 newfraction > oldfraction if(cha > -1 && cha < 0){//向右側(cè)滑動(dòng) //inmCustomAnimationInterface.onPrepareNextItemShowInScreen(view);//下一個(gè)Page將要進(jìn)入屏幕}else{//cha > 0 繼續(xù)向左側(cè)滑動(dòng) //outmCustomAnimationInterface.onPrepareCurrentItemLeaveScreen(view);//當(dāng)前Page將要離開(kāi)屏幕}}else{//起始時(shí),向右滑動(dòng),當(dāng)前Page中心位于屏幕中心右側(cè) //判斷滑動(dòng)趨勢(shì),如果繼續(xù)向右側(cè)滑動(dòng),position從0到-1越來(lái)越小 newfraction < oldfraction if(cha > -1 && cha < 0){//負(fù)值,繼續(xù)向右滑動(dòng),因此當(dāng)前page將要滑出屏幕 //outmCustomAnimationInterface.onPrepareCurrentItemLeaveScreen(view);//當(dāng)前Page將要離開(kāi)屏幕}else{//向左滑動(dòng) //inmCustomAnimationInterface.onPrepareNextItemShowInScreen(view);//下一個(gè)Page將要離開(kāi)屏幕}}}}}} boolean isApp,isDis; /*** Called each {@link #transformPage(View, float)} call after {@link #onTransform(View, float)} is finished.** @param view* @param position*/ protected void onPostTransform(View view, float position) { if(mCustomAnimationInterface != null){ if(position == -1 || position == 1){//當(dāng)前界面剛好完全移除界面mCustomAnimationInterface.onCurrentItemDisappear(view);isApp = true;}else if(position == 0){mCustomAnimationInterface.onNextItemAppear(view);//下一個(gè)Page剛好完全顯示isDis = true;} if(isApp && isDis){h.clear();isApp = false;isDis = false;}}} |
- BaseSliderView:
該類(lèi)就是SlideView的基類(lèi),持有SlideView的一些公共方法,比如設(shè)置SliderView的image資源的方法image(File file),圖片加載異常和失敗的處理,empty(URL)用于展示,errorDisappear(boolean disappear)。這種思想和一個(gè)EmptyView有些類(lèi)似,在我公司的項(xiàng)目也是使用了這種方式,比如通常一個(gè)ListView的界面,會(huì)有一個(gè)包裝后的progressbar(包含各種情況的處理)來(lái)顯示進(jìn)度,當(dāng)加載失敗的時(shí)候,或者無(wú)數(shù)據(jù)的時(shí)候,可以調(diào)用該progressbar身上的方法,去決定顯示何種布局。使用起來(lái)很方便。當(dāng)然如果你的SliderView更復(fù)雜,你可以通過(guò)實(shí)現(xiàn)BaseSliderView,然后實(shí)現(xiàn)自己的SliderView。
下面兩個(gè)類(lèi)就是切換的View:
DefaultSliderView:實(shí)現(xiàn)了BaseSliderView,該View默認(rèn)就是一張圖片
TextSliderView:帶有圖片和描述性文字的View
- Sliderlayout:
一個(gè)控制中心,將InfiniteViewPager,PageIndicator,粘合在一起,控制動(dòng)畫(huà)的樣式,ViewPager輪循的播放。
addSlider:向ViewPager中添加一個(gè)SlideView
startAutoCycle:啟動(dòng)輪播
pauseAutoCycle:停止輪播
setDuration: 輪播間隔
setPagerTransformer 為ViewPager設(shè)置自定義的PageTransformer
onInterceptTouchEvent 在用戶(hù)手勢(shì)按下的時(shí)候,就停止輪循
Note:
這里在onInterceptTouchEvent中處理而不能再onTouchEvent中處理。因?yàn)镾lideView會(huì)消費(fèi)掉點(diǎn)擊事件,事件被消費(fèi)了,沒(méi)法返回給SlideLayout的onTouchEvent中。
OK,總體設(shè)計(jì)弄清楚后,剩下來(lái)就是動(dòng)畫(huà)中最核心的部分了,就是動(dòng)畫(huà)效果的實(shí)現(xiàn),這里只簡(jiǎn)單的介紹Accordion動(dòng)畫(huà)的實(shí)現(xiàn),其它的大家可以自己分析。關(guān)于PropertyView動(dòng)畫(huà)的使用,可以參考我的另一篇文章:PropertyAnim實(shí)際應(yīng)用
Accordion
| 123456789 | public class AccordionTransformer extends BaseTransformer { @Override protected void onTransform(View view, float position) {ViewHelper.setPivotX(view,position < 0 ? 0 : view.getWidth());ViewHelper.setScaleX(view,position < 0 ? 1f + position : 1f - position);}} |
代碼家在README中注明了Thanks JazzyViewPager,開(kāi)始沒(méi)注意,后來(lái)看JazzyViewPager的源碼時(shí)候偶然發(fā)現(xiàn)AndroidImageSlider的一些實(shí)現(xiàn)的靈感是來(lái)自JazzyViewPager項(xiàng)目。不過(guò)JazzyViewPager的實(shí)現(xiàn)方式略顯復(fù)雜,沒(méi)有使用PageTransformer接口,而是使用了OnPageChangeListener接口的onPageScrolled方法。
下面簡(jiǎn)單的看下JazzyViewPager庫(kù)的動(dòng)畫(huà)實(shí)現(xiàn),它將positionOffset作為參數(shù)控制。由于positionOffset的值為[0,1),所以就需要分別處理正負(fù)的情況。另外JazzyViewPager是通過(guò)維護(hù)一個(gè)LinkedHashMap來(lái)持有Page的引用。在Adapter添加界面的時(shí)候,會(huì)調(diào)用JazzyViewPager的 setObjectForPosition(Object obj, int position) 方法存入到集合中去。然后在onPageScrolled方法中再根據(jù)position獲取當(dāng)前Page的前一個(gè)mLeft和后一個(gè)mRight界面,分別對(duì)前后兩個(gè)界面添加動(dòng)畫(huà)。
兩種方式的關(guān)鍵參數(shù)區(qū)別
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)方法的參數(shù):
| 1234567891011 | /*** This method will be invoked when the current page is scrolled, either as part* of a programmatically initiated smooth scroll or a user initiated touch scroll.** @param position Position index of the first page currently being displayed.* Page position+1 will be visible if positionOffset is nonzero.* @param positionOffset Value from [0, 1) indicating the offset from the page at position.* @param positionOffsetPixels Value in pixels indicating the offset from position.*/ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels); |
下面是打印的PageTransformer接口的transformPage時(shí)position的值:相鄰兩界面的絕對(duì)值的和為1.而正負(fù)則表示了滑入和滑出狀態(tài)
| 123456789101112131415161718 | 12-15 21:03:13.025 I/System.out﹕ transformPage---------------------- -0.43611112 12-15 21:03:13.025 I/System.out﹕ transformPage---------------------- 0.5638889 12-15 21:03:13.045 I/System.out﹕ transformPage---------------------- -0.38055557 12-15 21:03:13.045 I/System.out﹕ transformPage---------------------- 0.61944443 12-15 21:03:13.045 I/System.out﹕ transformPage---------------------- -0.37777779 12-15 21:03:13.045 I/System.out﹕ transformPage---------------------- 0.62222224 12-15 21:03:13.055 I/System.out﹕ transformPage---------------------- -0.32916668 12-15 21:03:13.055 I/System.out﹕ transformPage---------------------- 0.67083335 12-15 21:03:13.065 I/System.out﹕ transformPage---------------------- -0.32222223 12-15 21:03:13.065 I/System.out﹕ transformPage---------------------- 0.67777777 12-15 21:03:13.075 I/System.out﹕ transformPage---------------------- -0.27916667 12-15 21:03:13.075 I/System.out﹕ transformPage---------------------- 0.72083336 |
onPageScrolled方法的positionOffset的值為 [0, 1) ,而PageTransformer接口的transformPage(View page, float position) 已經(jīng)標(biāo)好了正負(fù)值(滑入和滑出),如果你的動(dòng)畫(huà)正好是相對(duì)的,那么用transfromPage就簡(jiǎn)單的多。
我們?nèi)⊥环N動(dòng)畫(huà)效果Cube來(lái)比較一下JazzyViewPager和AndroidImageSlider兩者的實(shí)現(xiàn):
JazzyViewPager部分源碼
| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 | @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { ... float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;mLeft = findViewFromObject(position);mRight = findViewFromObject(position+1); switch (mEffect) { case Standard: break;... case animateCube:animateCube(mLeft, mRight, effectOffset); break;}}... private void animateCube(View left, View right, float positionOffset, boolean in) { if (mState != State.IDLE) { if (left != null) {manageLayer(left, true);mRot = (in ? 90.0f : -90.0f) * positionOffset;ViewHelper.setPivotX(left, left.getMeasuredWidth());ViewHelper.setPivotY(left, left.getMeasuredHeight()*0.5f);ViewHelper.setRotationY(left, mRot);} if (right != null) {manageLayer(right, true);mRot = -(in ? 90.0f : -90.0f) * (1-positionOffset);ViewHelper.setPivotX(right, 0);ViewHelper.setPivotY(right, right.getMeasuredHeight()*0.5f);ViewHelper.setRotationY(right, mRot);}}} public void setObjectForPosition(Object obj, int position) {//每次Adapter實(shí)例化View的時(shí)候,調(diào)用該方法,將實(shí)例化的View存入集合mObjs.put(Integer.valueOf(position), obj);} public View findViewFromObject(int position) {//根據(jù)position從集合中獲取到對(duì)應(yīng)的ViewObject o = mObjs.get(Integer.valueOf(position)); if (o == null) { return null;}PagerAdapter a = getAdapter();View v; for (int i = 0; i < getChildCount(); i++) {v = getChildAt(i); if (a.isViewFromObject(v, o)) return v;} return null;}....} |
AndroidIamgeSlider中的CubeIn:
| 12345678910 | public class CubeInTransformer extends BaseTransformer { @Override protected void onTransform(View view, float position) { // Rotate the fragment on the left or right edgeViewHelper.setPivotX(view,position > 0 ? 0 : view.getWidth());ViewHelper.setPivotY(view,0);ViewHelper.setRotation(view,-90f * position);}} |
JazzyViewPager:CubeIn上面代碼中已經(jīng)貼出:
| 123456789 | if (left != null) {...mRot = (in ? 90.0f : -90.0f) * positionOffset;...}if (right != null) {mRot = -(in ? 90.0f : -90.0f) * (1-positionOffset);} |
可以精簡(jiǎn)為:
| 12 | positionOffset * value-(1-positionOffset) * value |
與從上面的transformPage打印的position的值再比較下
| 12 | 12-15 21:03:13.075 I/System.out﹕ transformPage---------------------- -0.27916667 12-15 21:03:13.075 I/System.out﹕ transformPage---------------------- 0.72083336 |
結(jié)論:
發(fā)現(xiàn)transformPage已經(jīng)幫我們處理好了一切,直接用。我們可以根據(jù)正負(fù)符號(hào)來(lái)判斷滑入和滑出的View(不用像JazzyViewPager那樣去維護(hù)一個(gè)集合了),從而針對(duì)滑入滑出做出不同或相對(duì)的的動(dòng)畫(huà)。最簡(jiǎn)單的,兩個(gè)界面如果是簡(jiǎn)單的相對(duì)動(dòng)畫(huà)(滑入對(duì)滑出),則什么都不用處理,直接用就行了,就像下面介紹的Yahoo視差的實(shí)現(xiàn)一樣,具體可以看下面的講解。
大家仔細(xì)看下相同的動(dòng)畫(huà)效果的處理方式,CubeInTransformer不用單獨(dú)處理上一個(gè)和下一個(gè)界面,而只管根據(jù)position的正負(fù)判斷當(dāng)前Page和下一個(gè)Page去自定義不同的動(dòng)畫(huà)即可。
所以可以把AndroidImageSlider中的一些動(dòng)畫(huà)效果看做是JazzyViewPager的精簡(jiǎn)版本。
對(duì)于ViewPager如何幫我們處理View的呢?可以看下源碼:
| 12345678910111213141516171819 | protected void onPageScrolled(int position, float offset, int offsetPixels) {...if (mPageTransformer != null) { final int scrollX = getScrollX(); final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.isDecor) continue; final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();mPageTransformer.transformPage(child, transformPos);}}...} |
小結(jié)
上面主要介紹了ViewPager的Page的滑動(dòng)動(dòng)畫(huà),兩種實(shí)現(xiàn)方式:
PageTransformer.transformPage方式:在執(zhí)行onPageScrolled方法的時(shí)候,會(huì)遍歷ViewPager的所有View,并執(zhí)行其transformPage方法。position是已經(jīng)處理好的(方向和值)
onPageScrolled方式:略顯復(fù)雜,因?yàn)闆](méi)法拿到View,還要自己去維護(hù)一個(gè)View集合,并且positionOffset的限制,需要自己去處理不同View的position : PageA:position , PageB:-(1-position)。
為ViewPager的Page內(nèi)容添加動(dòng)畫(huà),實(shí)現(xiàn)炫酷的交互效果
Tholotis
實(shí)現(xiàn)原理:
為Page內(nèi)部的View處理不同的平移速度,達(dá)到視差的效果,作者的代碼:
| 123456789101112131415161718192021222324252627282930313233343536 | 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);}} |
GitHub上類(lèi)似的效果還有:
https://github.com/prolificinteractive/ParallaxPager
https://github.com/flavienlaurent/discrollview
這兩個(gè)項(xiàng)目都封裝成了庫(kù),使用的時(shí)候也簡(jiǎn)單,但一般我們可以通過(guò)ViewPager.PageTransformer來(lái)實(shí)現(xiàn)這樣的效果,從上面AndroidImageSlider與JazzyViewPager的對(duì)比中也能看到,PageTransformer的實(shí)現(xiàn)方式更簡(jiǎn)單。而且ViewPager的動(dòng)畫(huà)庫(kù)也很多,比如上面的JazzyViewPager。你只需把某一個(gè)動(dòng)畫(huà)效果的transformPage方法的邏輯拿來(lái)就可用,當(dāng)然如果你的需求更復(fù)雜,或者ViewPager實(shí)現(xiàn)起來(lái)較麻煩,你可以考慮上面的兩個(gè)項(xiàng)目,擴(kuò)展自己的思維。
JazzyViewPager:
https://github.com/jfeinstein10/JazzyViewPager/blob/master/lib/src/com/jfeinstein/jazzyviewpager/JazzyViewPager.java
https://github.com/prolificinteractive/ParallaxPager
https://github.com/flavienlaurent/discrollview
Yahoo天氣
原理很簡(jiǎn)單,只是這次處理的動(dòng)畫(huà)對(duì)象是背景圖片,減慢其平移的速度,而ViewPager的內(nèi)容正常移動(dòng),從而達(dá)到視差的效果。
| 1234567891011121314151617181920212223 | public class ParallaxPageTransformer implements ViewPager.PageTransformer { 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(1); } else if (position <= 1) { // [-1,1] dummyImageView.setTranslationX(-position * (pageWidth / 2)); //Half the normal speed } else { // (1,+Infinity] // This page is way off-screen to the right.view.setAlpha(1);} }} |
OK,本來(lái)想自己寫(xiě),但剛巧在GitHub上發(fā)現(xiàn)了一個(gè):ParallaxPagerTransformer?,并提供了APK?,實(shí)現(xiàn)了Yahoo天氣的效果,但多了一些縮放的效果,將縮放的代碼注釋掉就和Yahoo天氣的效果完全一樣了。另外想clone到本地的朋友注意了,由于作者的AndroidStudio版本太老,導(dǎo)入的時(shí)候你需要做一些處理。
作者把該Transformer抽取出來(lái),做為了一個(gè)lib,就一個(gè)類(lèi),核心代碼很簡(jiǎn)單,說(shuō)明ViewPager.PageTransformer接口很強(qiáng)大啊:
| 1234567891011121314151617181920 | @Override public void transformPage(View view, float position) {View parallaxView = view.findViewById(id); if (parallaxView != null) { if (position > -1 && position < 1) { float width = parallaxView.getWidth();parallaxView.setTranslationX(-(position * width * speed)); float sc = ((float)view.getWidth() - border)/ view.getWidth(); if (position == 0) {//這里處理了縮放的效果,去掉即和Yahoo天氣的效果一樣view.setScaleX(1);view.setScaleY(1);} else {view.setScaleX(sc);view.setScaleY(sc);}}}} |
看了上面的實(shí)現(xiàn),大家應(yīng)該知道如何去實(shí)現(xiàn)類(lèi)似的交互動(dòng)畫(huà)了,而且現(xiàn)在的App導(dǎo)航頁(yè)中,也越來(lái)越多的使用到了這樣的交互,作者在文章還列舉了News Digest的導(dǎo)航頁(yè),有了思路,實(shí)現(xiàn)起來(lái)應(yīng)該就不難了,第一、三頁(yè)的動(dòng)畫(huà)上面已經(jīng)介紹過(guò),對(duì)于第二頁(yè),其實(shí)就是一個(gè)旋轉(zhuǎn)動(dòng)畫(huà)。實(shí)時(shí)的旋轉(zhuǎn)可以通過(guò)實(shí)時(shí)變化的position來(lái)處理。
類(lèi)似的項(xiàng)目:
https://github.com/andraskindler/parallaxviewpager
總結(jié):
整篇文章介紹的東西其實(shí)很簡(jiǎn)單,只是以前沒(méi)有研究過(guò)ViewPager.PageTransformer,使用簡(jiǎn)單,但功能強(qiáng)大,position參數(shù)在動(dòng)畫(huà)處理中相當(dāng)重要,因?yàn)槭菍?shí)時(shí)的百分比,所以省去了自己不少計(jì)算,如果你需要為ViewPager自定義動(dòng)畫(huà),那么選擇PageTransformer,對(duì)于本文的第二類(lèi)巧妙動(dòng)畫(huà)效果的介紹,看具體情況決定是否使用PageTransformer,如果position能很方便幫助動(dòng)畫(huà)的計(jì)算,那是最好的,如果不是那么你完全可以使用onPageScrolled來(lái)處理,源碼中也可看到,PageTransformer就是在onPageScroll中調(diào)用的。
使用時(shí)要注意,它只支持3.0以上的系統(tǒng)。如果要兼容,可以參考上面AndroidImageSlider的兼容實(shí)現(xiàn)。在文章中也插入了不少ViewPager動(dòng)畫(huà)項(xiàng)目的鏈接,也許不是使用PageTransformer,但也是一種思路的擴(kuò)展,供大家參考。下一篇文章,我會(huì)介紹Fragment的動(dòng)畫(huà),大家敬請(qǐng)期待。
參考文獻(xiàn):
官方文檔
Great animations with PageTransformer
Create ViewPager transitions
GitHub上相關(guān)的ViewPager動(dòng)畫(huà)的項(xiàng)目
https://github.com/daimajia/AndroidImageSlider
https://github.com/inovex/ViewPager3D
輪循的ViewPager
https://github.com/antonyt/InfiniteViewPager
https://github.com/JakeWharton/salvage
https://github.com/Trinea/android-auto-scroll-view-pager
VerticalViewPager
https://github.com/JakeWharton/Android-DirectionalViewPager
https://github.com/LambergaR/VerticalViewPager
https://github.com/VenomVendor/AutoNotifyViewPager
https://github.com/Dreddik/AndroidTouchGallery
特效的ViewPager
https://github.com/kmshack/Android-ParallaxHeaderViewPager
https://github.com/andraskindler/parallaxviewpager
https://github.com/MoshDev/BackgroundViewPager
與ViewPager一起使用的導(dǎo)航:
https://github.com/astuetz/PagerSlidingTabStrip?(不支持TextView顏色的變化)
https://github.com/jpardogo/PagerSlidingTabStrip?(支持TextView顏色變化)
https://github.com/DSofter/SmoothTabIndicator
FragmentAnim
https://github.com/DesarrolloAntonio/FragmentTransactionExtended
總結(jié)
以上是生活随笔為你收集整理的android ViewPager动画的实现原理及效果的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android 理解Fragment生命
- 下一篇: android中TextView分段显示