Android 自定義 ViewPager 打造千變萬化的圖片切換效果

出處:http://blog.csdn.net/lmj623565791/article/details/38026503 

記得第一次見到ViewPager這個控件,瞬間愛不釋手,做東西的主界面通通ViewPager,以及圖片切換也拋棄了ImageSwitch之類的,開始讓ViewPager來做。時間長了,ViewPager的切換效果覺得枯燥,形成了審美疲勞~~我們需要改變,今天教大家如何改變ViewPager切換時的效果,實現個性化的圖片切換~~

看一下這樣效果的圖片切換:


是不是比傳統的效果個性很多,嘿嘿~~其實很簡單,學習完這篇博客,保證你可以自定義切換效果,做出各種喪心病狂的切換~~

1、製作前的分析

觀察下效果圖,實際上改變的就是切換時的動畫,那麼簡單了,只需要用戶在切換時,拿到當前的View和下一個View,然後添加動畫是不是就可以了。好,第一步,獲取用戶切換時的當前View和切換至的目的View。

我們在來看一下,如果或者了當前View和目的View,對於動畫我們需要緩慢的變化,最好是根據用戶的手勢滑動。比如上述效果,用戶滑動時,目的圖片根據用戶滑動距離緩緩出現和慢慢變大。好,第二步,設計動畫的梯度變化。

經過分析,我們總結出兩個步驟,下面我們開始一步一步來打造~~~

2、獲取用戶切換時當前View和切換至的目的View。

ViewPager也需要監聽用戶的手勢,所以肯定提供了某個方法。於是縱觀ViewPager的方法,發現了一個叫做 onPageScrolled(int position, float positionOffset, int positionOffsetPixels)的方法~~

沒錯就是這個方法:在頁面滾動時調用~

下面仔細研究下這幾個參數:

直接說測試結果:

在非第一頁與最後一頁時,滑動到下一頁,position爲當前頁位置;滑動到上一頁:position爲當前頁-1

positionOffset 滑動到下一頁,[0,1)區間上變化;滑動到上一頁:(1,0]區間上變化

positionOffsetPixels這個和positionOffset很像:滑動到下一頁,[0,寬度)區間上變化;滑動到上一頁:(寬度,0]區間上變化

第一頁時:滑動到上一頁position=0 ,其他基本爲0 ;最後一頁滑動到下一頁 position爲當前頁位置,其他兩個參數爲0


豁然發現,我們需要的步驟的第二步解決了,positionOffset很適合作爲,漸變,縮放的控制參數;positionOffsetPixels則可以作爲平移等的控制參數。

那麼如何獲得當前View和目的View呢:

分享幾個我的歧途:

1、【錯誤】我通過getChildAt(position),getChildAt(position+1),getChildAt(position-1)獲得滑動時,左右的兩個View;乍一看,還真覺得不錯~~~在代碼寫出來,再乍效果也出不來~~錯誤原因:我們忽略一個特別大的東西,ViewPager的機制,滑動時動態加載和刪除View,ViewPager其實只會維持2到3個View,而position的範圍基本屬於無限~~

2、【錯誤】我通過getCurrentItem獲得當前的位置,然後+1,-1獲得後一個或者前一個~~正在竊喜,趕快代碼改過來,效果怎麼也不對,亂七八糟的~~仔細觀察日誌,這個getCurrentItem當用戶手指離開的屏幕,Page還在動畫執行時,就改變了~~難怪~整個滑動過程並不是固定的~~唉,心都碎了~

3、【錯誤】position在整個滑動的過程中是不變化的,而且ViewPager會保存2個或3個View;那麼我考慮,如果是第一頁、或者最後一頁那麼我取getChildAt(0)和getChildAt(1),如果在其他頁面則爲getChildAt(0),getChildAt(2),然後經過一系列的變化~我想這會總該對了吧,於是我遇到第一問題,第一頁的時候,不管左右position都爲0,尼瑪,這哪個爲左View,哪個爲右View~~

說了這麼多錯誤,大家可以繞過這些彎路,也能從這些彎路里面看出點什麼~

下面說正確的,其實ViewPager在添加一個View或者銷燬一個View時,是我們自己的PageAdapter中控制的,於是我們可以在ViewPager裏面維繫一個HashMap<Position,View>,然後滑動的時候,通過get(position)取出,比如上述效果,始終是右邊的View變化,要麼從小到大,要麼從大到小

那麼滑倒下一頁:左邊的View:map.get(position) ,右邊的View : map.get(position+1) . 

那麼滑倒上一頁:左邊的View : map.get(position) , 右邊的View : map.get(position+1) , 一樣的,因爲滑到上一頁,position爲當前頁-1

好了,至此,我們分析了且解決了所有步驟。

3、代碼

MainActivity

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.example.zhy_jazzyviewpager;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.support.v4.view.PagerAdapter;  
  6. import android.view.Menu;  
  7. import android.view.View;  
  8. import android.view.ViewGroup;  
  9. import android.widget.ImageView;  
  10. import android.widget.ImageView.ScaleType;  
  11.   
  12. public class MainActivity extends Activity  
  13. {  
  14.     protected static final String TAG = "MainActivity";  
  15.     private int[] mImgIds;  
  16.     private MyJazzyViewPager mViewPager;  
  17.   
  18.     @Override  
  19.     protected void onCreate(Bundle savedInstanceState)  
  20.     {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.         mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,  
  24.                 R.drawable.d };  
  25.         mViewPager = (MyJazzyViewPager) findViewById(R.id.id_viewPager);  
  26.         mViewPager.setAdapter(new PagerAdapter()  
  27.         {  
  28.   
  29.             @Override  
  30.             public boolean isViewFromObject(View arg0, Object arg1)  
  31.             {  
  32.                 return arg0 == arg1;  
  33.             }  
  34.   
  35.             @Override  
  36.             public void destroyItem(ViewGroup container, int position,  
  37.                     Object object)  
  38.             {  
  39.                 container.removeView((View) object);  
  40.             }  
  41.   
  42.             @Override  
  43.             public Object instantiateItem(ViewGroup container, int position)  
  44.             {  
  45.                 ImageView imageView = new ImageView(MainActivity.this);  
  46.                 imageView.setImageResource(mImgIds[position]);  
  47.                 imageView.setScaleType(ScaleType.CENTER_CROP);  
  48.                 container.addView(imageView);  
  49.                 mViewPager.setObjectForPosition(imageView, position);  
  50.                 return imageView;  
  51.             }  
  52.   
  53.             @Override  
  54.             public int getCount()  
  55.             {  
  56.                 return mImgIds.length;  
  57.             }  
  58.         });  
  59.   
  60.     }  
  61.   
  62. }  

這個很常見的代碼,就是初始化ViewPager~~就沒啥可說的了~~有一點需要注意:在instantiateItem方法,我們多調用了一個mViewPager.setObjectForPosition(imageView, position);其實就是爲了給我們的Map存值

主要看自定義的ViewPager

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.example.zhy_jazzyviewpager;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.LinkedHashMap;  
  5.   
  6. import android.content.Context;  
  7. import android.support.v4.view.ViewPager;  
  8. import android.util.AttributeSet;  
  9. import android.util.Log;  
  10. import android.view.View;  
  11.   
  12. import com.nineoldandroids.view.ViewHelper;  
  13.   
  14. public class MyJazzyViewPager extends ViewPager  
  15. {  
  16.     private float mTrans;  
  17.     private float mScale;  
  18.     /** 
  19.      * 最大的縮小比例 
  20.      */  
  21.     private static final float SCALE_MAX = 0.5f;  
  22.     private static final String TAG = "MyJazzyViewPager";  
  23.     /** 
  24.      * 保存position與對於的View 
  25.      */  
  26.     private HashMap<Integer, View> mChildrenViews = new LinkedHashMap<Integer, View>();  
  27.     /** 
  28.      * 滑動時左邊的元素 
  29.      */  
  30.     private View mLeft;  
  31.     /** 
  32.      * 滑動時右邊的元素 
  33.      */  
  34.     private View mRight;  
  35.   
  36.     public MyJazzyViewPager(Context context, AttributeSet attrs)  
  37.     {  
  38.         super(context, attrs);  
  39.     }  
  40.   
  41.     @Override  
  42.     public void onPageScrolled(int position, float positionOffset,  
  43.             int positionOffsetPixels)  
  44.     {  
  45.   
  46. //      Log.e(TAG, "position=" + position+", positionOffset = "+positionOffset+" ,positionOffsetPixels =  " + positionOffsetPixels+" , currentPos = " + getCurrentItem());  
  47.           
  48.         //滑動特別小的距離時,我們認爲沒有動,可有可無的判斷  
  49.         float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;  
  50.           
  51.         //獲取左邊的View  
  52.         mLeft = findViewFromObject(position);  
  53.         //獲取右邊的View  
  54.         mRight = findViewFromObject(position + 1);  
  55.           
  56.         // 添加切換動畫效果  
  57.         animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);  
  58.         super.onPageScrolled(position, positionOffset, positionOffsetPixels);  
  59.     }  
  60.   
  61.     public void setObjectForPosition(View view, int position)  
  62.     {  
  63.         mChildrenViews.put(position, view);  
  64.     }  
  65.   
  66.     /** 
  67.      * 通過過位置獲得對應的View 
  68.      *  
  69.      * @param position 
  70.      * @return 
  71.      */  
  72.     public View findViewFromObject(int position)  
  73.     {  
  74.         return mChildrenViews.get(position);  
  75.     }  
  76.   
  77.     private boolean isSmall(float positionOffset)  
  78.     {  
  79.         return Math.abs(positionOffset) < 0.0001;  
  80.     }  
  81.   
  82.     protected void animateStack(View left, View right, float effectOffset,  
  83.             int positionOffsetPixels)  
  84.     {  
  85.         if (right != null)  
  86.         {  
  87.             /** 
  88.              * 縮小比例 如果手指從右到左的滑動(切換到後一個):0.0~1.0,即從一半到最大 
  89.              * 如果手指從左到右的滑動(切換到前一個):1.0~0,即從最大到一半 
  90.              */  
  91.             mScale = (1 - SCALE_MAX) * effectOffset + SCALE_MAX;  
  92.             /** 
  93.              * x偏移量: 如果手指從右到左的滑動(切換到後一個):0-720 如果手指從左到右的滑動(切換到前一個):720-0 
  94.              */  
  95.             mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;  
  96.             ViewHelper.setScaleX(right, mScale);  
  97.             ViewHelper.setScaleY(right, mScale);  
  98.             ViewHelper.setTranslationX(right, mTrans);  
  99.         }  
  100.         if (left != null)  
  101.         {  
  102.             left.bringToFront();  
  103.         }  
  104.     }  
  105. }  

可以看到,核心代碼都是onPageScrolled,我們通過findViewFromObject(position); findViewFromObject(position + 1);分別獲取了左右兩邊的View,然後添加動畫效果;當前這個例子添加了兩個動畫,一個是從0.5放大到1.0或者1.0縮小到0.5,沒錯由我們的positionOffset提供梯度的變化~~還有個平移的動畫:下一頁直接移動到當前屏幕(默認是在右邊,可以註釋這個效果,怎麼運行看看),然後不斷的通過positionOffsetPixels抵消原來默認移動時的位移,讓用戶感覺它就在原地放大縮小~~

好了,這樣就實現了~~你可以隨便寫自己喜歡的動畫效果,比如在默認上面加個淡入淡出或者神馬,隨便~~是不是很隨意~~

我們的佈局文件:

[html] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.   >  
  6.   
  7.     <com.example.zhy_jazzyviewpager.MyJazzyViewPager  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:id="@+id/id_viewPager" />  
  11.   
  12. </RelativeLayout>  


4、JazzyViewPager的使用

其實上面的實現就是github上JazzyViewPager的源碼,用法不用說了,就是我們的MainActivity,它內置了大概10來種效果,我們可以通過代碼或者佈局上面設置動畫效果~~我們上面的例子效果,它叫做Stack;

使用JazzViewPager的代碼:其實基本一樣~~最後也會貼上JazzyViewPager的源碼的下載

MainActivity

[java] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. package com.jfeinstein.jazzyviewpager;  
  2.   
  3. import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;  
  4.   
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.support.v4.view.PagerAdapter;  
  8. import android.view.View;  
  9. import android.view.ViewGroup;  
  10. import android.widget.ImageView;  
  11. import android.widget.ImageView.ScaleType;  
  12.   
  13. public class MainActivity extends Activity  
  14. {  
  15.     protected static final String TAG = "MainActivity";  
  16.     private int[] mImgIds;  
  17.     private JazzyViewPager mViewPager;  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState)  
  21.     {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.         mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,  
  25.                 R.drawable.d };  
  26.         mViewPager = (JazzyViewPager) findViewById(R.id.id_viewPager);  
  27.         //設置切換效果  
  28.         mViewPager.setTransitionEffect(TransitionEffect.Stack);  
  29.           
  30.           
  31.         mViewPager.setAdapter(new PagerAdapter()  
  32.         {  
  33.   
  34.             @Override  
  35.             public boolean isViewFromObject(View arg0, Object arg1)  
  36.             {  
  37.                 return arg0 == arg1;  
  38.             }  
  39.   
  40.             @Override  
  41.             public void destroyItem(ViewGroup container, int position,  
  42.                     Object object)  
  43.             {  
  44.                 container.removeView((View) object);  
  45.             }  
  46.   
  47.             @Override  
  48.             public Object instantiateItem(ViewGroup container, int position)  
  49.             {  
  50.                 ImageView imageView = new ImageView(MainActivity.this);  
  51.                 imageView.setImageResource(mImgIds[position]);  
  52.                 imageView.setScaleType(ScaleType.CENTER_CROP);  
  53.                 container.addView(imageView);  
  54.                 mViewPager.setObjectForPosition(imageView, position);  
  55.                 return imageView;  
  56.             }  
  57.   
  58.             @Override  
  59.             public int getCount()  
  60.             {  
  61.                 return mImgIds.length;  
  62.             }  
  63.         });  
  64.   
  65.     }  
  66.   
  67. }  

與我們的代碼唯一區別就是:

//設置切換效果
mViewPager.setTransitionEffect(TransitionEffect.Stack);

它有12中可選的切換效果,其實就是寫了12個切換的動畫~~~

好了,最後附上一個我比較喜歡的效果:Tablet


最後,喜歡藉此博客拋磚引玉~~大家對感興趣的github上的代碼,可以進行分析與自己嘗試去實現,有時候會發現不是很難~你也可以做到~!


源碼點擊下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章