Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 自定義 ViewPager 打造千變萬化的圖片切換效果淺析

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

編輯:關於Android編程

記得第一次見到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,然後滑動的時候,通過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 plain copy print?在CODE上查看代碼片派生到我的代碼片    
  1. packagecom.example.zhy_jazzyviewpager;
  2. importandroid.app.Activity;importandroid.os.Bundle;
  3. importandroid.support.v4.view.PagerAdapter;importandroid.view.Menu;
  4. importandroid.view.View;importandroid.view.ViewGroup;
  5. importandroid.widget.ImageView;importandroid.widget.ImageView.ScaleType;
  6. publicclassMainActivityextendsActivity
  7. {protectedstaticfinalStringTAG="MainActivity";
  8. privateint[]mImgIds;privateMyJazzyViewPagermViewPager;
  9. @Override
  10. protectedvoidonCreate(BundlesavedInstanceState){
  11. super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
  12. mImgIds=newint[]{R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d};
  13. mViewPager=(MyJazzyViewPager)findViewById(R.id.id_viewPager);mViewPager.setAdapter(newPagerAdapter()
  14. {
  15. @OverridepublicbooleanisViewFromObject(Viewarg0,Objectarg1)
  16. {returnarg0==arg1;
  17. }
  18. @OverridepublicvoiddestroyItem(ViewGroupcontainer,intposition,
  19. Objectobject){
  20. container.removeView((View)object);}
  21. @Override
  22. publicObjectinstantiateItem(ViewGroupcontainer,intposition){
  23. ImageViewimageView=newImageView(MainActivity.this);imageView.setImageResource(mImgIds[position]);
  24. imageView.setScaleType(ScaleType.CENTER_CROP);container.addView(imageView);
  25. mViewPager.setObjectForPosition(imageView,position);returnimageView;
  26. }
  27. @OverridepublicintgetCount()
  28. {returnmImgIds.length;
  29. }});
  30. }
  31. }  
    package com.example.zhy_jazzyviewpager;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v4.view.PagerAdapter;
    import android.view.Menu;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.ImageView.ScaleType;
    
    public class MainActivity extends Activity
    {
    	protected static final String TAG = "MainActivity";
    	private int[] mImgIds;
    	private MyJazzyViewPager mViewPager;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
    				R.drawable.d };
    		mViewPager = (MyJazzyViewPager) findViewById(R.id.id_viewPager);
    		mViewPager.setAdapter(new PagerAdapter()
    		{
    
    			@Override
    			public boolean isViewFromObject(View arg0, Object arg1)
    			{
    				return arg0 == arg1;
    			}
    
    			@Override
    			public void destroyItem(ViewGroup container, int position,
    					Object object)
    			{
    				container.removeView((View) object);
    			}
    
    			@Override
    			public Object instantiateItem(ViewGroup container, int position)
    			{
    				ImageView imageView = new ImageView(MainActivity.this);
    				imageView.setImageResource(mImgIds[position]);
    				imageView.setScaleType(ScaleType.CENTER_CROP);
    				container.addView(imageView);
    				mViewPager.setObjectForPosition(imageView, position);
    				return imageView;
    			}
    
    			@Override
    			public int getCount()
    			{
    				return mImgIds.length;
    			}
    		});
    
    	}
    
    }
    

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

     

    主要看自定義的ViewPager

     

    [java] view plain copy print?在CODE上查看代碼片派生到我的代碼片    
    1. packagecom.example.zhy_jazzyviewpager;
    2. importjava.util.HashMap;importjava.util.LinkedHashMap;
    3. importandroid.content.Context;
    4. importandroid.support.v4.view.ViewPager;importandroid.util.AttributeSet;
    5. importandroid.util.Log;importandroid.view.View;
    6. importcom.nineoldandroids.view.ViewHelper;
    7. publicclassMyJazzyViewPagerextendsViewPager
    8. {privatefloatmTrans;
    9. privatefloatmScale;/**
    10. *最大的縮小比例*/
    11. privatestaticfinalfloatSCALE_MAX=0.5f;privatestaticfinalStringTAG="MyJazzyViewPager";
    12. /***保存position與對於的View
    13. */privateHashMapmChildrenViews=newLinkedHashMap();
    14. /***滑動時左邊的元素
    15. */privateViewmLeft;
    16. /***滑動時右邊的元素
    17. */privateViewmRight;
    18. publicMyJazzyViewPager(Contextcontext,AttributeSetattrs)
    19. {super(context,attrs);
    20. }
    21. @OverridepublicvoidonPageScrolled(intposition,floatpositionOffset,
    22. intpositionOffsetPixels){
    23. //Log.e(TAG,"position="+position+",positionOffset="+positionOffset+",positionOffsetPixels="+positionOffsetPixels+",currentPos="+getCurrentItem());
    24. //滑動特別小的距離時,我們認為沒有動,可有可無的判斷
    25. floateffectOffset=isSmall(positionOffset)?0:positionOffset;
    26. //獲取左邊的ViewmLeft=findViewFromObject(position);
    27. //獲取右邊的ViewmRight=findViewFromObject(position+1);
    28. //添加切換動畫效果
    29. animateStack(mLeft,mRight,effectOffset,positionOffsetPixels);super.onPageScrolled(position,positionOffset,positionOffsetPixels);
    30. }
    31. publicvoidsetObjectForPosition(Viewview,intposition){
    32. mChildrenViews.put(position,view);}
    33. /**
    34. *通過過位置獲得對應的View*
    35. *@paramposition*@return
    36. */publicViewfindViewFromObject(intposition)
    37. {returnmChildrenViews.get(position);
    38. }
    39. privatebooleanisSmall(floatpositionOffset){
    40. returnMath.abs(positionOffset)<0.0001;}
    41. protectedvoidanimateStack(Viewleft,Viewright,floateffectOffset,
    42. intpositionOffsetPixels){
    43. if(right!=null){
    44. /***縮小比例如果手指從右到左的滑動(切換到後一個):0.0~1.0,即從一半到最大
    45. *如果手指從左到右的滑動(切換到前一個):1.0~0,即從最大到一半*/
    46. mScale=(1-SCALE_MAX)*effectOffset+SCALE_MAX;/**
    47. *x偏移量:如果手指從右到左的滑動(切換到後一個):0-720如果手指從左到右的滑動(切換到前一個):720-0*/
    48. mTrans=-getWidth()-getPageMargin()+positionOffsetPixels;ViewHelper.setScaleX(right,mScale);
    49. ViewHelper.setScaleY(right,mScale);ViewHelper.setTranslationX(right,mTrans);
    50. }if(left!=null)
    51. {left.bringToFront();
    52. }}
    53. }  
      package com.example.zhy_jazzyviewpager;
      
      import java.util.HashMap;
      import java.util.LinkedHashMap;
      
      import android.content.Context;
      import android.support.v4.view.ViewPager;
      import android.util.AttributeSet;
      import android.util.Log;
      import android.view.View;
      
      import com.nineoldandroids.view.ViewHelper;
      
      public class MyJazzyViewPager extends ViewPager
      {
      	private float mTrans;
      	private float mScale;
      	/**
      	 * 最大的縮小比例
      	 */
      	private static final float SCALE_MAX = 0.5f;
      	private static final String TAG = "MyJazzyViewPager";
      	/**
      	 * 保存position與對於的View
      	 */
      	private HashMap mChildrenViews = new LinkedHashMap();
      	/**
      	 * 滑動時左邊的元素
      	 */
      	private View mLeft;
      	/**
      	 * 滑動時右邊的元素
      	 */
      	private View mRight;
      
      	public MyJazzyViewPager(Context context, AttributeSet attrs)
      	{
      		super(context, attrs);
      	}
      
      	@Override
      	public void onPageScrolled(int position, float positionOffset,
      			int positionOffsetPixels)
      	{
      
      //		Log.e(TAG, "position=" + position+", positionOffset = "+positionOffset+" ,positionOffsetPixels =  " + positionOffsetPixels+" , currentPos = " + getCurrentItem());
      		
      		//滑動特別小的距離時,我們認為沒有動,可有可無的判斷
      		float effectOffset = isSmall(positionOffset) ? 0 : positionOffset;
      		
      		//獲取左邊的View
      		mLeft = findViewFromObject(position);
      		//獲取右邊的View
      		mRight = findViewFromObject(position + 1);
      		
      		// 添加切換動畫效果
      		animateStack(mLeft, mRight, effectOffset, positionOffsetPixels);
      		super.onPageScrolled(position, positionOffset, positionOffsetPixels);
      	}
      
      	public void setObjectForPosition(View view, int position)
      	{
      		mChildrenViews.put(position, view);
      	}
      
      	/**
      	 * 通過過位置獲得對應的View
      	 * 
      	 * @param position
      	 * @return
      	 */
      	public View findViewFromObject(int position)
      	{
      		return mChildrenViews.get(position);
      	}
      
      	private boolean isSmall(float positionOffset)
      	{
      		return Math.abs(positionOffset) < 0.0001;
      	}
      
      	protected void animateStack(View left, View right, float effectOffset,
      			int positionOffsetPixels)
      	{
      		if (right != null)
      		{
      			/**
      			 * 縮小比例 如果手指從右到左的滑動(切換到後一個):0.0~1.0,即從一半到最大
      			 * 如果手指從左到右的滑動(切換到前一個):1.0~0,即從最大到一半
      			 */
      			mScale = (1 - SCALE_MAX) * effectOffset + SCALE_MAX;
      			/**
      			 * x偏移量: 如果手指從右到左的滑動(切換到後一個):0-720 如果手指從左到右的滑動(切換到前一個):720-0
      			 */
      			mTrans = -getWidth() - getPageMargin() + positionOffsetPixels;
      			ViewHelper.setScaleX(right, mScale);
      			ViewHelper.setScaleY(right, mScale);
      			ViewHelper.setTranslationX(right, mTrans);
      		}
      		if (left != null)
      		{
      			left.bringToFront();
      		}
      	}
      }
      

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

       

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

      我們的布局文件:

       

      [html] view plain copy print?在CODE上查看代碼片派生到我的代碼片    
      1. android:layout_width="match_parent"android:layout_height="match_parent"
      2. >
      3. android:layout_height="wrap_content"android:id="@+id/id_viewPager"/>
      4.  
        
        
            
        
        
        


         

        4、JazzyViewPager的使用

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

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

        MainActivity

         

        [java] view plain copy print?在CODE上查看代碼片派生到我的代碼片    
        1. packagecom.jfeinstein.jazzyviewpager;
        2. importcom.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;
        3. importandroid.app.Activity;importandroid.os.Bundle;
        4. importandroid.support.v4.view.PagerAdapter;importandroid.view.View;
        5. importandroid.view.ViewGroup;importandroid.widget.ImageView;
        6. importandroid.widget.ImageView.ScaleType;
        7. publicclassMainActivityextendsActivity{
        8. protectedstaticfinalStringTAG="MainActivity";privateint[]mImgIds;
        9. privateJazzyViewPagermViewPager;
        10. @OverrideprotectedvoidonCreate(BundlesavedInstanceState)
        11. {super.onCreate(savedInstanceState);
        12. setContentView(R.layout.activity_main);mImgIds=newint[]{R.drawable.a,R.drawable.b,R.drawable.c,
        13. R.drawable.d};mViewPager=(JazzyViewPager)findViewById(R.id.id_viewPager);
        14. //設置切換效果mViewPager.setTransitionEffect(TransitionEffect.Stack);
        15.  
        16. mViewPager.setAdapter(newPagerAdapter(){
        17. @Override
        18. publicbooleanisViewFromObject(Viewarg0,Objectarg1){
        19. returnarg0==arg1;}
        20. @Override
        21. publicvoiddestroyItem(ViewGroupcontainer,intposition,Objectobject)
        22. {container.removeView((View)object);
        23. }
        24. @OverridepublicObjectinstantiateItem(ViewGroupcontainer,intposition)
        25. {ImageViewimageView=newImageView(MainActivity.this);
        26. imageView.setImageResource(mImgIds[position]);imageView.setScaleType(ScaleType.CENTER_CROP);
        27. container.addView(imageView);mViewPager.setObjectForPosition(imageView,position);
        28. returnimageView;}
        29. @Override
        30. publicintgetCount(){
        31. returnmImgIds.length;}
        32. });
        33. }
        34. }  
          package com.jfeinstein.jazzyviewpager;
          
          import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;
          
          import android.app.Activity;
          import android.os.Bundle;
          import android.support.v4.view.PagerAdapter;
          import android.view.View;
          import android.view.ViewGroup;
          import android.widget.ImageView;
          import android.widget.ImageView.ScaleType;
          
          public class MainActivity extends Activity
          {
          	protected static final String TAG = "MainActivity";
          	private int[] mImgIds;
          	private JazzyViewPager mViewPager;
          
          	@Override
          	protected void onCreate(Bundle savedInstanceState)
          	{
          		super.onCreate(savedInstanceState);
          		setContentView(R.layout.activity_main);
          		mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,
          				R.drawable.d };
          		mViewPager = (JazzyViewPager) findViewById(R.id.id_viewPager);
          		//設置切換效果
          		mViewPager.setTransitionEffect(TransitionEffect.Stack);
          		
          		
          		mViewPager.setAdapter(new PagerAdapter()
          		{
          
          			@Override
          			public boolean isViewFromObject(View arg0, Object arg1)
          			{
          				return arg0 == arg1;
          			}
          
          			@Override
          			public void destroyItem(ViewGroup container, int position,
          					Object object)
          			{
          				container.removeView((View) object);
          			}
          
          			@Override
          			public Object instantiateItem(ViewGroup container, int position)
          			{
          				ImageView imageView = new ImageView(MainActivity.this);
          				imageView.setImageResource(mImgIds[position]);
          				imageView.setScaleType(ScaleType.CENTER_CROP);
          				container.addView(imageView);
          				mViewPager.setObjectForPosition(imageView, position);
          				return imageView;
          			}
          
          			@Override
          			public int getCount()
          			{
          				return mImgIds.length;
          			}
          		});
          
          	}
          
          }
          

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

           

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

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

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

          \

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

           

          源碼點擊下載

           

    1. 上一頁:
    2. 下一頁:
    熱門文章
    閱讀排行版
    Copyright © Android教程網 All Rights Reserved