Android为ViewPager增加切换动画——自定义ViewPager

分析

首先我们分析一下实现自定义ViewPager自带切换动画效果时,我们需要拿到一些什么?第一,我们知道ViewPager来回切换的时候,其实就是不同的View在屏幕上切换,所以我们在自定义ViewPager的时候需要拿到当前正在切换的2个View。第二,既然我们要为ViewPager添加动画,那么我们就需要自定义动画。好,以上两个点都是在这个自定义ViewPager中被需要的东西,我们一步步来创建和获取到吧。

接下来,我新建一个类ViewPagerWithTransformer继承自ViewPager,来实现自定义的ViewPager。首先,我们重写一下ViewPager中的一个方法,代码如下,我们在方法中打印一下方法中的参数,在布局文件中使用这个自定义的ViewPager,在Activity中为这个ViewPager增加数据适配器,添加3个ImageView到ViewPager中,实现ViewPager上的切换,代码可以见上一篇博客中MainActivity中。

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. protected void onPageScrolled(int position, float offset, int offsetPixels) {  
  3.     super.onPageScrolled(position, offset, offsetPixels);  
  4.     Log.i("TAG""position = " + position + ",offset = " + offset + ",offsetPixels = " + offsetPixels);  
  5. }  

上面方法中的参数已经被打印了,LOG输出日志在下面,首先看一下从第0页滑动到第1页的LOG:


然后,再看一下从第1页滑动到第0页的LOG:


分析一下上面的LOG日志:

当第0页-->第1页:position从0-->1;offset从0-->1;offsetPixels从0-->屏幕最大宽度

当第1页-->第0页:position从1-->0;offset从1-->0;offsetPixels从0-->屏幕最大宽度

明显可以感知,只有position代表的是当前正在切换View的位置,offset表示View的偏移比重,offsetPixels表示View实际的偏移量(px)。


获取切换中的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从缓存集合中移除。具体部分代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. // 保存ViewPager上View的对象  
  2. private Map<Integer, View> mViewCache = new HashMap<Integer, View>();  
  3.   
  4. /** 
  5.  * 设置View的缓存 
  6.  *  
  7.  * @param position 
  8.  * @param viewCache 
  9. */  
  10. public void setViewCache(Integer position, View viewCache) {  
  11.     mViewCache.put(position, viewCache);  
  12. }  
  13.   
  14. /** 
  15.  * 从View的缓存集合中移除指定position的View 
  16.  *  
  17.  * @param position 
  18.  */  
  19. public void removeViewCache(Integer position) {  
  20.     mViewCache.remove(position);  
  21. }  


切换动画的实现

上面解决了一个头疼的问题,如果获取正在切换中的View对象,这个问题已经在上面解决掉了,那么下面就只剩下动画了。我们在这个案例模仿上篇博客中的DepthPageTransformer动画效果吧,我们自定义两种动画,一个Scale动画和一个Translate动画,使两个动画交替一起执行就可以模仿出DepthPageTransformer这种平移缩放的动画效果出来,代码很简单,不单独贴出了,下面有完整的代码的。

完整的代码

自定义ViewPager的源码,ViewPagerWithTransformer.java
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class ViewPagerWithTransformer extends ViewPager {  
  2.   
  3.     // 左边的View  
  4.     private View mLeftView;  
  5.     // 右边的View  
  6.     private View mRightView;  
  7.     // 平移动画的梯度值  
  8.     private float mTranslate;  
  9.     // 缩放动画的梯度值  
  10.     private float mScale;  
  11.     // 最小缩放比例  
  12.     private static final float MIN_SCALE = 0.6f;  
  13.     // 保存ViewPager上View的对象  
  14.     private Map<Integer, View> mViewCache = new HashMap<Integer, View>();  
  15.   
  16.     /** 
  17.      * 设置View的缓存 
  18.      *  
  19.      * @param position 
  20.      * @param viewCache 
  21.      */  
  22.     public void setViewCache(Integer position, View viewCache) {  
  23.         mViewCache.put(position, viewCache);  
  24.     }  
  25.   
  26.     /** 
  27.      * 从View的缓存集合中移除指定position的View 
  28.      *  
  29.      * @param position 
  30.      */  
  31.     public void removeViewCache(Integer position) {  
  32.         mViewCache.remove(position);  
  33.     }  
  34.   
  35.     public ViewPagerWithTransformer(Context context, AttributeSet attrs) {  
  36.         super(context, attrs);  
  37.         // TODO Auto-generated constructor stub  
  38.     }  
  39.   
  40.     @Override  
  41.     protected void onPageScrolled(int position, float offset, int offsetPixels) {  
  42.         super.onPageScrolled(position, offset, offsetPixels);  
  43.         mLeftView = mViewCache.get(position);  
  44.         mRightView = mViewCache.get(position + 1);  
  45.         animStack(mLeftView, mRightView, offset, offsetPixels);  
  46.     }  
  47.       
  48.     private void animStack(View left, View right, float offset, int offsetPixels) {  
  49.         // TODO Auto-generated method stub  
  50.         if (right != null) {  
  51.             // 从第0页——》到第1页,offset:0~1,缩放比例mScale:0.6~1  
  52.             mScale = (1 - MIN_SCALE) * offset + MIN_SCALE;  
  53.             //平移的距离  
  54.             mTranslate = -getWidth() - getPageMargin() + offsetPixels;  
  55.   
  56.             //使用NineOldAndroids编写属性动画  
  57.             ViewHelper.setScaleX(right, mScale);  
  58.             ViewHelper.setScaleY(right, mScale);  
  59.             ViewHelper.setTranslationX(right, mTranslate);  
  60.         }  
  61.         if (left != null) {  
  62.             // 左边的页永远在最上面  
  63.             left.bringToFront();  
  64.         }  
  65.     }  
  66. }  
在布局文件中申明,activity_main.xml
[html]  view plain copy print ? 在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.     <com.example.viewpagerwithtransformeranim.ViewPagerWithTransformer  
  7.         android:id="@+id/viewpager"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent" >  
  10.     </com.example.viewpagerwithtransformeranim.ViewPagerWithTransformer>  
  11.   
  12. </RelativeLayout>  
在Activity中使用这个控件,MainActivity.java
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     private int[] imgRes = new int[] { R.drawable.guide_image1, R.drawable.guide_image2, R.drawable.guide_image3 };  
  4.     private List<ImageView> imgList = new ArrayList<ImageView>();  
  5.     private ViewPagerWithTransformer mViewPager;  
  6.   
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  11.         setContentView(R.layout.activity_main);  
  12.         mViewPager = (ViewPagerWithTransformer) findViewById(R.id.viewpager);  
  13.         mViewPager.setAdapter(new PagerAdapter() {  
  14.   
  15.             @Override  
  16.             public boolean isViewFromObject(View arg0, Object arg1) {  
  17.                 return arg0 == arg1;  
  18.             }  
  19.   
  20.             @Override  
  21.             public int getCount() {  
  22.                 return imgRes.length;  
  23.             }  
  24.   
  25.             @Override  
  26.             public Object instantiateItem(ViewGroup container, int position) {  
  27.                 ImageView mImageView = new ImageView(MainActivity.this);  
  28.                 mImageView.setBackgroundResource(imgRes[position]);  
  29.                 mImageView.setScaleType(ScaleType.CENTER_CROP);  
  30.                 imgList.add(mImageView);  
  31.                 //给ViewPager设置View缓存  
  32.                 mViewPager.setViewCache(position, mImageView);  
  33.                 container.addView(mImageView);  
  34.                 return mImageView;  
  35.             }  
  36.   
  37.             @Override  
  38.             public void destroyItem(ViewGroup container, int position, Object object) {  
  39.                 //移除ViewPager的View缓存  
  40.                 mViewPager.removeViewCache(position);  
  41.                 container.removeView(imgList.get(position));  
  42.             }  
  43.         });  
  44.     }  
  45. }  
好,全部源码在上面了,注意一下,在给ViewPager设置的适配器PagerAdapter里面重写instantiateItem方法时,一定要记得调用mViewPager.setViewCache(position, mImageView)方法,给ViewPager设置View缓存,然后在重写destroyItem方法中调用mViewPager.removeViewCache(position)方法移除View缓存。
感谢CSDN博客专家 鸿洋无私奉献的教程,教程视频地址: http://www.imooc.com/learn/226


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值