轉載請註明出處: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
【錯誤】上面咱們分析咱們再本身定義一個帶動畫切換的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); }
咱們在這個案例模仿上篇博客中的DepthPageTransformer動畫效果吧。咱們本身定義兩種動畫。一個Scale動畫和一個Translate動畫,使兩個動畫交替一塊兒運行就可以模仿出DepthPageTransformer這種平移縮放的動畫效果出來,代碼很是easy,不單獨貼出了。如下有完整的代碼的。
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緩存。