Android爲ViewPager添加切換動畫——本身定義ViewPager

轉載請註明出處:http://blog.csdn.net/allen315410/article/details/44224517html

在上篇博客中,我寫了一個使用屬性動畫爲ViewPager加入切換動畫的方法。並且可以兼容到Android3.0如下版本號的設備上。那麼關於爲ViewPager加入動畫的方式還會有第二種實現方案。就是本身定義一個本身帶動畫效果的ViewPager,關於上篇博客,還沒來得及查看的朋友可以點擊這裏進行查看。如下。咱們將新建一個project。來講說如何本身定義一個自帶切換動畫效果的ViewPager。java


分析

首先咱們分析一下實現本身定義ViewPager自帶切換動畫效果時,咱們需要拿到一些什麼?第一,咱們知道ViewPager來回切換的時候。事實上就是不一樣的View在屏幕上切換。因此咱們在本身定義ViewPager的時候需要拿到當前正在切換的2個View。第二,既然咱們要爲ViewPager加入動畫。那麼咱們就需要本身定義動畫。好,以上兩個點都是在這個本身定義ViewPager中被需要的東西,咱們一步步來建立和獲取到吧。android

接下來。我新建一個類ViewPagerWithTransformer繼承自ViewPager。來實現本身定義的ViewPager。首先。咱們重寫一下ViewPager中的一個方法,代碼例如如下。咱們在方法中打印一下方法中的參數,在佈局文件裏使用這個本身定義的ViewPager。在Activity中爲這個ViewPager添加數據適配器。加入3個ImageView到ViewPager中,實現ViewPager上的切換,代碼可以見上一篇博客中MainActivity中。緩存

@Override
protected void onPageScrolled(int position, float offset, int offsetPixels) {
	super.onPageScrolled(position, offset, offsetPixels);
	Log.i("TAG", "position = " + position + ",offset = " + offset + ",offsetPixels = " + offsetPixels);
}

上面方法中的參數已經被打印了,LOG輸出日誌在如下,首先看一下從第0頁滑動到第1頁的LOG:ide


而後,再看一下從第1頁滑動到第0頁的LOG:佈局


分析一下上面的LOG日誌:post

當第0頁-->第1頁:position從0-->1;offset從0-->1;offsetPixels從0-->屏幕最大寬度動畫

當第1頁-->第0頁:position從1-->0;offset從1-->0;offsetPixels從0-->屏幕最大寬度ui

明顯可以感知,僅僅有position表明的是當前正在切換View的位置,offset表示View的偏移比重,offsetPixels表示View實際的偏移量(px)。this


獲取切換中的View對象

【錯誤】上面咱們分析咱們再本身定義一個帶動畫切換的ViewPager時,需要獲得正在切換中的2個View對象。那麼咱們該怎麼在代碼中獲取到這2個View的對象呢?你們知道,上篇博客中咱們使用了ViewPager的源代碼。ViewPager的父類是ViewGroup,咱們知道在ViewGroup中想要獲取View對象的話,有個getChildAt(int index)方法可以獲得View對象。這種方法在ViewGroup中使用是沒有問題的,而後在ViewPager中使用就會出錯了。因爲ViewPager中加入了緩存機制。一個ViewPager默認僅僅能緩存2-3個View的對象,當有不少其它的View對象被載入到ViewPager中時。ViewPager會捨棄掉前面一個View對象,這樣就致使咱們不能經過getChildAt(position)、getChildAt(position-1)、getChildAt(position+1)方法來獲取上一個、當前、下一個View對象了,顯然position表明的View對象不止兩三個,而是無限的。

【錯誤】經過getChildAt(position)顯然獲取不到咱們想要的對象了,既然不能使用position的話。ViewGroup中另外一個getCurrentItem()獲取當前View的角標和getChildCount()方法獲取View數量的方法。是否是可以用呢?咱們先在onPageScrolled()方法中打印一下LOG日誌吧。


咱們可以看到,getCurrentItem的值一直在不斷的變化着,當咱們打開第1頁時。getCurrentItem=0,這是咱們可以依據這個getCurrentItem的值帶入getChildAt(getCurrentItem-1)、getChildAt(getCurrentItem)、getChildAt(getCurrentItem+1)中。確實可以獲取到上一個View。當前View、下一個View。

而後不幸的是,當咱們手指從屏幕中放開的時候。getCurrentItem變成了1。這時前面經過getChildAt(getCurrentItem)獲取到的View就不正確了。

因此依據getCurrentItem()獲取View也是不正確的。

【正確】經過上面2種方法均不能準確的獲取到當前的View對象了。咱們再來細看一下position這個參數。結合上面的LOG輸入看一下。當咱們在ViewPager中使得第0頁滑向第1頁的時候。position是沒有變化的,此時pisition=0;當咱們從第1頁滑到第0頁的時候,position還沒沒有變化,此時position=0;由此咱們是否是仍是可以依據position來肯定View的對象。咱們若是position表明第0頁的View,那麼position+1就是表明第1頁的View了。以此類推就可以獲得全部的View了。如下,咱們就可以在代碼中去實現咱們這種想法了。也特別的簡單,就是建立一個Map集合用來爲View作一個緩存機制,咱們使用position做爲鍵,使用view的對象做爲值,並且向外提供一個加入View緩存的方法和一個移除View緩存的方法。當咱們爲ViewPager提供適配器PagerAdapter時,可以在instantiateItem(ViewGroup container, int position)方法中將View對象設置進View的緩存集合中。在destroyItem(ViewGroup container, int position, Object object)方法中將View從緩存集合中移除。詳細部分代碼例如如下:

// 保存ViewPager上View的對象
private Map<Integer, View> mViewCache = new HashMap<Integer, View>();

/**
 * 設置View的緩存
 * 
 * @param position
 * @param viewCache
*/
public void setViewCache(Integer position, View viewCache) {
	mViewCache.put(position, viewCache);
}

/**
 * 從View的緩存集合中移除指定position的View
 * 
 * @param position
 */
public void removeViewCache(Integer position) {
	mViewCache.remove(position);
}


切換動畫的實現

上面攻克了一個頭疼的問題。若是獲取正在切換中的View對象,這個問題已經在上面解決掉了,那麼如下就僅僅剩下動畫了。

咱們在這個案例模仿上篇博客中的DepthPageTransformer動畫效果吧。咱們本身定義兩種動畫。一個Scale動畫和一個Translate動畫,使兩個動畫交替一塊兒運行就可以模仿出DepthPageTransformer這種平移縮放的動畫效果出來,代碼很是easy,不單獨貼出了。如下有完整的代碼的。


完整的代碼

本身定義ViewPager的源代碼。ViewPagerWithTransformer.java
public class ViewPagerWithTransformer extends ViewPager {

	// 左邊的View
	private View mLeftView;
	// 右邊的View
	private View mRightView;
	// 平移動畫的梯度值
	private float mTranslate;
	// 縮放動畫的梯度值
	private float mScale;
	// 最小縮放比例
	private static final float MIN_SCALE = 0.6f;
	// 保存ViewPager上View的對象
	private Map<Integer, View> mViewCache = new HashMap<Integer, View>();

	/**
	 * 設置View的緩存
	 * 
	 * @param position
	 * @param viewCache
	 */
	public void setViewCache(Integer position, View viewCache) {
		mViewCache.put(position, viewCache);
	}

	/**
	 * 從View的緩存集合中移除指定position的View
	 * 
	 * @param position
	 */
	public void removeViewCache(Integer position) {
		mViewCache.remove(position);
	}

	public ViewPagerWithTransformer(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	@Override
	protected void onPageScrolled(int position, float offset, int offsetPixels) {
		super.onPageScrolled(position, offset, offsetPixels);
		mLeftView = mViewCache.get(position);
		mRightView = mViewCache.get(position + 1);
		animStack(mLeftView, mRightView, offset, offsetPixels);
	}
	
	private void animStack(View left, View right, float offset, int offsetPixels) {
		// TODO Auto-generated method stub
		if (right != null) {
			// 從第0頁——》到第1頁,offset:0~1。縮放比例mScale:0.6~1
			mScale = (1 - MIN_SCALE) * offset + MIN_SCALE;
			//平移的距離
			mTranslate = -getWidth() - getPageMargin() + offsetPixels;

			//使用NineOldAndroids編寫屬性動畫
			ViewHelper.setScaleX(right, mScale);
			ViewHelper.setScaleY(right, mScale);
			ViewHelper.setTranslationX(right, mTranslate);
		}
		if (left != null) {
			// 左邊的頁永遠在最上面
			left.bringToFront();
		}
	}
}
在佈局文件裏申明,activity_main.xml
<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" >

    <com.example.viewpagerwithtransformeranim.ViewPagerWithTransformer
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.example.viewpagerwithtransformeranim.ViewPagerWithTransformer>

</RelativeLayout>
在Activity中使用這個控件。MainActivity.java
public class MainActivity extends Activity {

	private int[] imgRes = new int[] { R.drawable.guide_image1, R.drawable.guide_image2, R.drawable.guide_image3 };
	private List<ImageView> imgList = new ArrayList<ImageView>();
	private ViewPagerWithTransformer mViewPager;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		mViewPager = (ViewPagerWithTransformer) findViewById(R.id.viewpager);
		mViewPager.setAdapter(new PagerAdapter() {

			@Override
			public boolean isViewFromObject(View arg0, Object arg1) {
				return arg0 == arg1;
			}

			@Override
			public int getCount() {
				return imgRes.length;
			}

			@Override
			public Object instantiateItem(ViewGroup container, int position) {
				ImageView mImageView = new ImageView(MainActivity.this);
				mImageView.setBackgroundResource(imgRes[position]);
				mImageView.setScaleType(ScaleType.CENTER_CROP);
				imgList.add(mImageView);
				//給ViewPager設置View緩存
				mViewPager.setViewCache(position, mImageView);
				container.addView(mImageView);
				return mImageView;
			}

			@Override
			public void destroyItem(ViewGroup container, int position, Object object) {
				//移除ViewPager的View緩存
				mViewPager.removeViewCache(position);
				container.removeView(imgList.get(position));
			}
		});
	}
}
好,全部源代碼在上面了,注意一下,在給ViewPager設置的適配器PagerAdapter裏面重寫instantiateItem方法時。必定要記得調用mViewPager.setViewCache(position, mImageView)方法,給ViewPager設置View緩存。而後在重寫destroyItem方法中調用mViewPager.removeViewCache(position)方法移除View緩存。

感謝CSDN博客專家 鴻洋 無私奉獻的教程。教程視頻地址: http://www.imooc.com/learn/226

相關文章
相關標籤/搜索