【Android進階】自定義帶動畫切換效果的ViewPager

1.引言

ViewPager是我們比較熟悉的一個控件,用於視圖切換或作爲首頁的切換,默認是沒有動畫切換效果的,當然我們可以通過setPageTransform來添加各種各樣的動畫,這裏我們介紹另外一種方法,自定義自帶動畫切換效果的ViewPager。

2.準備工作

首先分析我們要實現動畫切換效果,我們必須要準備的工作:

1.首先必須獲取兩個切換的視圖,mLeftView 和 mRightView。

2.然後還必須有一個變化的梯度值,用於控制切換過程中的值變化。

新建一個類 ViewPagerWithTransformAnim.java 繼承自ViewPager

public class ViewPageWithTransformAnim extends ViewPager{
	//兩個View
	private View mLeft;
	private View mRight;
	//變化的梯度
	private float mTrans;
	//縮放的值
	private float mScale;
	//縮放的最小值,從0.5 到 1
	private static final float MIN_SCALE = 0.5f;
	public ViewPageWithTransformAnim(Context context) {
		super(context);
		
	}
	public ViewPageWithTransformAnim(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onPageScrolled(int position, float offset, int pix) {
		Log.i("meng.li", "position = "+position +" offset = "+offset+" pix = "+pix);
		super.onPageScrolled(position, offset, pix);
	}
}
ViewPager中有一個onPageScrolled方法,我們可以在這裏獲取我們需要的東西。

我們觀察onPageScrolled方法的log信息:

從第0頁滑動到第1頁

從第1頁回到第0頁


總結下規律

0頁到1頁  position  0 ; offset  0-1

1頁到0頁  position 0  ; offset   1-0

下面仔細研究下這幾個參數:
直接說測試結果:
在非第一頁與最後一頁時,滑動到下一頁,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
好了,至此,我們分析了且解決了所有步驟

代碼: MainActivity.java

public class MainActivity extends Activity {
//	private ViewPager viewPager;
	//這裏是爲了兼容3.0版本下設置動畫效果從新改寫的ViewPager
//	private ViewPagerCompat viewPager;
	//自定義帶動畫切換效果的ViewPager
	private ViewPageWithTransformAnim viewPager;
	private int[] mImgId = new int[]{R.drawable.guide_image1,R.drawable.guide_image2,R.drawable.guide_image3};
	List<ImageView> mImages = new ArrayList<ImageView>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager = (ViewPageWithTransformAnim)findViewById(R.id.view_pager);
        viewPager.setAdapter(new PagerAdapter() {
        	//實例化 Item 創建ImageView
			@Override
			public Object instantiateItem(ViewGroup container, int position) {
				ImageView imageView = new ImageView(MainActivity.this);
				imageView.setImageResource(mImgId[position]);
				imageView.setScaleType(ScaleType.CENTER_CROP);
				container.addView(imageView);
				mImages.add(imageView);
				//添加到View中
				viewPager.setViewForPosition(imageView,position);
				return imageView;
			}
			@Override
			public boolean isViewFromObject(View arg0, Object arg1) {
				//判斷view是否來自於 arg1 arg1 是初始化的時候添加進去的
				return arg0 == arg1;
			}
			
			@Override
			public int getCount() {
				//獲取視圖的長度
				return mImgId.length;
			}
			@Override
			public void destroyItem(ViewGroup container, int position,
					Object object) {
				//移除視圖
				viewPager.removeViewFromPosition(position);
				//移除視圖
				container.removeView(mImages.get(position));
			}
		});
        
    }
}

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

自定義ViewPager

/*
 * 自定義的帶動畫切換效果的ViewPager
 */
/*需要有哪些準備
 * a.需要拿到當前切換的兩個 View
 * b.需要拿到一個動畫的梯度值
 * Translation 和 Scale
 */
public class ViewPageWithTransformAnim extends ViewPager{
	private Map<Integer, View>  vChildren= new HashMap<Integer, View>();
	public void setViewForPosition(View view,int position){
		vChildren.put(position, view);
	}
	public void removeViewFromPosition(int position){
		vChildren.remove(position);
	}
	//兩個View
	private View mLeft;
	private View mRight;
	//變化的梯度
	private float mTrans;
	//縮放的值
	private float mScale;
	//縮放的最小值,從0.5 到 1
	private static final float MIN_SCALE = 0.5f;
	public ViewPageWithTransformAnim(Context context) {
		super(context);
		
	}
	public ViewPageWithTransformAnim(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onPageScrolled(int position, float offset, int pix) {
//		Log.i("meng.li", "position = "+position +" offset = "+offset+" pix = "+pix);
		mLeft = vChildren.get(position);
		mRight = vChildren.get(position+1);
		//添加動畫效果
		animStack(mLeft,mRight,offset,pix);
		Log.i("meng.li", getCurrentItem() +" -- "+getChildCount());
		super.onPageScrolled(position, offset, pix);
	}
	private void animStack(View left,View right,float offset,int offsetPixels){
		if(right != null){
			//從0頁到1頁, offset: 0-1
			//從0 逐漸變到1
			mScale = (1 - MIN_SCALE)* offset + MIN_SCALE;
			//從 0 到寬度的變化
			mTrans = -getWidth() -getPageMargin() + offsetPixels;
			
			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抵消原來默認移動時的位移,讓用戶感覺它就在原地放大縮小~~

佈局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.viewpageranimation.MainActivity" >
    <com.example.viewpageranimation.ViewPageWithTransformAnim
         android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>
實現的效果爲:

4、JazzyViewPager的使用
其實上面的實現就是github上JazzyViewPager的源碼,用法不用說了,就是我們的MainActivity,它內置了大概10來種效果,我們可以通過代碼或者佈局上面設置動畫效果~~我們上面的例子效果,它叫做Stack;
使用JazzViewPager的代碼:其實基本一樣~~最後也會貼上JazzyViewPager的源碼的下載

MainActivity

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

JazzyViewPager 下載地址:https://github.com/jfeinstein10/JazzyViewPager

本文轉載自:http://blog.csdn.net/lmj623565791/article/details/38026503



發佈了53 篇原創文章 · 獲贊 39 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章