首页 > 移动平台 > 详细

Android为ViewPager增加切换动画——使用属性动画

时间:2016-01-10 21:10:36      阅读:164      评论:0      收藏:0      [点我收藏+]

转自:http://blog.csdn.net/allen315410/article/details/44200623

 

ViewPager作为Android最常用的的组件之一,相信大家在项目中会频繁的使用到的,例如利用ViewPager制作引导页、轮播图,甚至做整个app的表现层的框架等等。

 

Android3.0以下不支持切换动画

但是在Android 3.0(API 11)以下的ViewPager是比较死板的,不支持动画特效的,这也就让ViewPager在切换的时候达不到很好的用户体验,下面就是Android3.0以下不添加动画的ViewPager的实现代码以及效果演示:

 

  1. public class MainActivity extends Activity {  
  2.   
  3.     private ViewPager mViewPager;  
  4.     private int[] imgRes = new int[] { R.drawable.guide_image1, R.drawable.guide_image2, R.drawable.guide_image3 };  
  5.     private List<ImageView> imgList = new ArrayList<ImageView>();  
  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 = (ViewPager) 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.                 container.addView(mImageView);  
  32.                 return mImageView;  
  33.             }  
  34.   
  35.             @Override  
  36.             public void destroyItem(ViewGroup container, int position, Object object) {  
  37.                 container.removeView(imgList.get(position));  
  38.             }  
  39.         });  
  40.     }  
  41. }  

上面是最简单的ViewPager使用的Demo,运行如下,看起来很普通很死板:

 

技术分享

 

支持Android3.0以上的官方方法

值得庆幸的是,Google在Android3.0以上的版本中增加了给ViewPager设置切换动画的API,允许开发者在Android3.0以上版本的应用中为ViewPager增加动画切换效果,这样就可以让ViewPager的切换效果变的绚丽点了,为ViewPager添加动画效果的API如下:
  1. public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer){...}  
其中第一个参数boolean类型设置true就好,第二个参数PageTransformer就是我们自定义好的动画效果:
  1. mViewPager = (ViewPager) findViewById(R.id.viewpager);  
  2. mViewPager.setPageTransformer(true, new ZoomOutPageTransformer());  
其中ZoomOutPageTransformer的代码来自于google的training文档中,英文好的朋友可以直接进入文档查看,链接是
源码如下:
  1. public class ZoomOutPageTransformer implements ViewPager.PageTransformer {  
  2.     private static final float MIN_SCALE = 0.85f;  
  3.     private static final float MIN_ALPHA = 0.5f;  
  4.   
  5.     public void transformPage(View view, float position) {  
  6.         int pageWidth = view.getWidth();  
  7.         int pageHeight = view.getHeight();  
  8.   
  9.         if (position < -1) { // [-Infinity,-1)  
  10.             // This page is way off-screen to the left.  
  11.             view.setAlpha(0);  
  12.   
  13.         } else if (position <= 1) { // [-1,1]  
  14.             // Modify the default slide transition to shrink the page as well  
  15.             float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));  
  16.             float vertMargin = pageHeight * (1 - scaleFactor) / 2;  
  17.             float horzMargin = pageWidth * (1 - scaleFactor) / 2;  
  18.             if (position < 0) {  
  19.                 view.setTranslationX(horzMargin - vertMargin / 2);  
  20.             } else {  
  21.                 view.setTranslationX(-horzMargin + vertMargin / 2);  
  22.             }  
  23.   
  24.             // Scale the page down (between MIN_SCALE and 1)  
  25.             view.setScaleX(scaleFactor);  
  26.             view.setScaleY(scaleFactor);  
  27.   
  28.             // Fade the page relative to its size.  
  29.             view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));  
  30.   
  31.         } else { // (1,+Infinity]  
  32.             // This page is way off-screen to the right.  
  33.             view.setAlpha(0);  
  34.         }  
  35.     }  
  36. }  
另外Google文档中还提供了另外一个动画的实现方式,我暂且把源码附在下面:
  1. public class DepthPageTransformer implements ViewPager.PageTransformer {  
  2.     private static final float MIN_SCALE = 0.75f;  
  3.   
  4.     public void transformPage(View view, float position) {  
  5.         int pageWidth = view.getWidth();  
  6.   
  7.         if (position < -1) { // [-Infinity,-1)  
  8.             // This page is way off-screen to the left.  
  9.             view.setAlpha(0);  
  10.   
  11.         } else if (position <= 0) { // [-1,0]  
  12.             // Use the default slide transition when moving to the left page  
  13.             view.setAlpha(1);  
  14.             view.setTranslationX(0);  
  15.             view.setScaleX(1);  
  16.             view.setScaleY(1);  
  17.   
  18.         } else if (position <= 1) { // (0,1]  
  19.             // Fade the page out.  
  20.             view.setAlpha(1 - position);  
  21.   
  22.             // Counteract the default slide transition  
  23.             view.setTranslationX(pageWidth * -position);  
  24.   
  25.             // Scale the page down (between MIN_SCALE and 1)  
  26.             float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));  
  27.             view.setScaleX(scaleFactor);  
  28.             view.setScaleY(scaleFactor);  
  29.   
  30.         } else { // (1,+Infinity]  
  31.             // This page is way off-screen to the right.  
  32.             view.setAlpha(0);  
  33.         }  
  34.     }  
  35. }  
两种方式的所实现的效果如下所示,一是ZoomOutPageTransformer,二是DepthPageTransformer
技术分享        技术分享
 

兼容Android3.0以下的版本

前面我们说过的,Android3.0以下版本是不支持ViewPager增加切换动画的,原因很简单,我们可以参考一下上面贴出的两段代码,可以看到两段代码中都是使用了Android的属性动画写的切换效果,我们知道Android属性动画是在Android3.0才出来的特性,仅支持Android3.0及其以上版本,所以ViewPager的切换动画在Android3.0以下的版本中就不会支持了。分析出了不能在Android3.0以下版本中添加动画的原因后,我们就可以通过其它的方法来解决这个兼容性的问题了。
还记得之前我们在博客中就聊过老外的一个大牛——JakeWharton,对的,这个大牛在GitHub开源了自己为Android3.0以下系统添加属性动画的项目——NineOldAndroids,我们可以下载源码或者jar导入到我们的工程中来,用它来兼容我们的Android3.0以下的版本。
JakeWharton的主页是:https://github.com/JakeWharton
下面就是我在DepthPageTransformer类中将View换成NineOldAndroids下的ViewHelper后,修改可以兼容Android3.0以下版本的属性动画:
  1. public class DepthPageTransformer implements ViewPager.PageTransformer {  
  2.     private static final float MIN_SCALE = 0.75f;  
  3.   
  4.     public void transformPage(View view, float position) {  
  5.         int pageWidth = view.getWidth();  
  6.   
  7.         if (position < -1) {  
  8.             // view.setAlpha(0);  
  9.             ViewHelper.setAlpha(view, 0);  
  10.         } else if (position <= 0) {  
  11.             // view.setAlpha(1);  
  12.             ViewHelper.setAlpha(view, 1);  
  13.             // view.setTranslationX(0);  
  14.             ViewHelper.setTranslationX(view, 0);  
  15.             // view.setScaleX(1);  
  16.             ViewHelper.setScaleX(view, 1);  
  17.             // view.setScaleY(1);  
  18.             ViewHelper.setScaleY(view, 1);  
  19.         } else if (position <= 1) {  
  20.             // view.setAlpha(1 - position);  
  21.             ViewHelper.setAlpha(view, 1 - position);  
  22.             // view.setTranslationX(pageWidth * -position);  
  23.             ViewHelper.setTranslationX(view, pageWidth * -position);  
  24.             float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));  
  25.             // view.setScaleX(scaleFactor);  
  26.             ViewHelper.setScaleX(view, scaleFactor);  
  27.             // view.setScaleY(scaleFactor);  
  28.             ViewHelper.setScaleY(view, scaleFactor);  
  29.         } else {  
  30.             // view.setAlpha(0);  
  31.             ViewHelper.setAlpha(view, 0);  
  32.         }  
  33.     }  
  34. }  
为了好理解,我没有将Google提供的源码删除,而是注释掉了,方便大家进行比较阅读,然而即使这样使用了NineOldAndroids对我们的动画源码进行了改造,当我们打开一个Android2.3的模拟器运行一下的时候发现,没有起到效果,也就是这个动画效果并没有执行,在模拟器上运行的ViewPager滑动的效果还是原始默认的左右来回切换的动画,这又是怎么回事呢?为止,我们需要打开ViewPager的源码进行阅读一下了,既然是在mViewPager.setPageTransformer(true, new DepthPageTransformer());这句代码没有起到效果,那么我们就点进去查看一下setPageTransformer这个方法的源码,源码如下:
  1. public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer){  
  2.     if (Build.VERSION.SDK_INT >= 11) {  
  3.         boolean hasTransformer = transformer != null;  
  4.         boolean needsPopulate = hasTransformer != (this.mPageTransformer != null);  
  5.         this.mPageTransformer = transformer;  
  6.         setChildrenDrawingOrderEnabledCompat(hasTransformer);  
  7.         if (hasTransformer) {  
  8.             this.mDrawingOrder = (reverseDrawingOrder ? 2 : 1);  
  9.          } else {  
  10.             this.mDrawingOrder = 0;  
  11.         }  
  12.         if (needsPopulate)   
  13.             populate();  
  14.         }  
  15. }  
好了,源码一目了然,我们分析到在ViewPager中的setPageTransFormer这个方法中,首先判断了一下当前设备的Build.VERSION_SDK_INT>=11,也就是说当前设备是Android3.0以上系统的话,这个方法体执行没问题,但是若是3.0一下,那就抱歉,无法执行了。知道这个原因之后我们就需要解决这个问题了,我在这里修改了一下ViewPager的源码,修改ViewPager的源码之前,读者可以先去下载一份ViewPager的源码,然后拷贝到工程中,重新命名一下,将里面的if判断语句给删除了。以下就是我修改后的部分源码,命名为ViewPagerCompat:
  1. public void setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer) {  
  2.     // if (Build.VERSION.SDK_INT >= 11) {  
  3.     final boolean hasTransformer = transformer != null;  
  4.     final boolean needsPopulate = hasTransformer != (mPageTransformer != null);  
  5.     mPageTransformer = transformer;  
  6.     setChildrenDrawingOrderEnabledCompat(hasTransformer);  
  7.     if (hasTransformer) {  
  8.         mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;  
  9.     } else {  
  10.         mDrawingOrder = DRAW_ORDER_DEFAULT;  
  11.     }  
  12.     if (needsPopulate)  
  13.         populate();  
  14.     // }  
  15. }  
好,源码修改完毕,将您的源码中的ViewPager替换成这个修改后的源码ViewPagerCompat,那么ViewPager切换动画的效果就出现了。
 

分析动画源码,实现自己的动画效果

上面的动画源码是我在Google的官方文档中找到的,里面完整的实现了一个系列动画的功能。那么我们是不是可以也能通过分析一下Google文档中源码的写法,找到规律后,我们自己动手实现一个自己的动画效果呢?先来看一下DepthPageTransformer.java这段源码,这个类实现了一个接口ViewPager.PageTransformer,重写了其中的一个方法
  1. public void transformPage(View view, float position) {  
  2.     ...  
  3.     Log.i("TAG", "view = " + view + ",position = " + position);   
  4.         ...  
  5. }  
如上所示,我先在这个方法中打印出方法中的参数view对象和position的值,然后我们来看一下LOG的输出:
技术分享
好,我们看LOG输出日志,可以看到有2个view对象在不停交错的输出,其中我选中标注的view的哈希值是4054c260,未选中标注的view的哈希值是4054c548,然后再来看一下position的值,可以注意的是,4054c260的view的position值从0.0一直不断减小到-1.0,4054c548的view的position的值从1.0一直不断减小到0.0,我们为了方便,记哈希值为4054c260的view为A页,记哈希值为4054c548的view为B页。
情景分析一下,也请一边看一下DepthPageTransformer.java的源码;
当position < -1,此时position均不代表A页和B页的位置,所以这里我们不做任何的动画出来。
当position <= 0,此时position的范围是在0.0 ~-1.0之间,可以认为这是代表A页的运动轨迹,也就是A页移出屏幕外。
当position <= 1,此时position的范围是在1.0 ~ 0.0之间,可以认为这是代表B页的运动轨迹,也就是B页移到屏幕上。
当position > 1,此时position均不代表A页和B页的位置,所以这里我们不做任何的动画出来。
 
好了,通过我们的分析可知,我们实现动画效果是根据position的变化来设定的。那么,我们现在也要根据position来实现一个自己的ViewPager的动画效果了,我们做一个ViewPager的旋转移出屏幕和旋转移到屏幕上的效果吧。草图如下所示:
技术分享
看着草图,我们想象一下,当手指向左滑动的时候,A页会旋转一定的角度移出屏幕,B页也会旋转一定的角度移到屏幕上,那么我们就先给移动的角度设置一个常量吧,这里我假设旋转的最大角度是20度了,以下是我的源码:
  1. /** 
  2.  * ViewPager自定义旋转动画 
  3.  *  
  4.  * @author Vincent 
  5.  *  
  6.  */  
  7. public class RotateDownTransformer implements PageTransformer {  
  8.   
  9.     // 旋转的最大角度为20度  
  10.     private static final float MAX_ROTATE = 20.0f;  
  11.     // 旋转过程中的角度  
  12.     private float currentRotate;  
  13.   
  14.     @Override  
  15.     public void transformPage(View view, float position) {  
  16.         int pageWidth = view.getWidth();  
  17.         Log.i("TAG", "view = " + view + ",position = " + position);  
  18.         if (position < -1) {  
  19.             ViewHelper.setRotation(view, 0);  
  20.         } else if (position <= 0) {  
  21.             // position范围[-1.0,0.0],此时A页动画移出屏幕  
  22.             currentRotate = position * MAX_ROTATE;  
  23.             // 设置当前页的旋转中心点,横坐标是屏幕宽度的1/2,纵坐标为屏幕的高度  
  24.             ViewHelper.setPivotX(view, pageWidth / 2);  
  25.             ViewHelper.setPivotY(view, view.getHeight());  
  26.             ViewHelper.setRotation(view, currentRotate);  
  27.         } else if (position <= 1) {  
  28.             // position范围(0.0,1.0],此时B页动画移到屏幕  
  29.             currentRotate = position * MAX_ROTATE;  
  30.             // 设置当前页的旋转中心点,横坐标是屏幕宽度的1/2,纵坐标为屏幕的高度  
  31.             ViewHelper.setPivotX(view, pageWidth / 2);  
  32.             ViewHelper.setPivotY(view, view.getHeight());  
  33.             ViewHelper.setRotation(view, currentRotate);  
  34.         } else {  
  35.             ViewHelper.setRotation(view, 0);  
  36.         }  
  37.     }  
  38. }  
技术分享
效果图就在上面哦,看起来还凑合吧,根据上面的描述,只要你知道属性动画的相关api,也可以自定义初各种各样的动画效果出来,可以将多种单一的动画效果混合在一起使用,让ViewPager的滑动效果看起来更加的“复杂”。

Android为ViewPager增加切换动画——使用属性动画

原文:http://www.cnblogs.com/wllearnandroid/p/5119115.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!