Android好奇宝宝_07_ViewPager切换动画(兼容低版本)

闲着无聊,写写Demo

想着写一个图片轮播,百度了一下基本都是用ViewPager实现的,那就用ViewPager来练手。

写完了再自定义切换效果,发现3.0以下不兼容,只好想办法。

先上效果图:

下面一步一步来:

(1)写布局:

<RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <jjj.demo.viewpagerdemo.AnimViewPager
                android:id="@+id/test_viewPager"
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:background="#f7f7f7" >
            </jjj.demo.viewpagerdemo.AnimViewPager>

            <jjj.demo.viewpagerdemo.DianDian
                android:id="@+id/test_dianDian"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentTop="true"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="270dp"
                app:count="5" >
            </jjj.demo.viewpagerdemo.DianDian>
        </RelativeLayout>

AnimViewPager是我重写的ViewPager,解决低版本Viewpager不支持自定义切换动画,这里先将其当成普通的ViewPager就行了。

DianDian是我很久以前写得一个自定义控件,就是底部那5个点。

简单说下原理:

DianDian继承了LinearLayout,通过GradientDrawable这个类来生成选中和没选中时的点的drawable,通过Margin控制间隔距离,然后添加到LinearLayout里面就行了。

(2)为ViewPager填充内容:

		viewPager = (AnimViewPager) findViewById(R.id.test_viewPager);
		initIamgeList();
		viewPager.setAdapter(mAdapter);

initImageList()初始化一个长度为5的ArrayList<ImageView>做测试用。

mAdapter跟网上其它demo一样,没什么特别的:

PagerAdapter mAdapter = new PagerAdapter() {
		public void destroyItem(android.view.ViewGroup container, int position, Object object) {
			((AnimViewPager) container).removeView(imageViewsList.get(position));
		};

		public Object instantiateItem(android.view.ViewGroup container, int position) {
			((AnimViewPager) container).addView(imageViewsList.get(position));
			return imageViewsList.get(position);
		};

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

		@Override
		public int getCount() {
			return 5;
		}
	};

(3)设置监听

设置Pager切换监听,以切换底下的5个点的显示。

	OnPageChangeListener mChangeListener = new OnPageChangeListener() {
		@Override
		public void onPageSelected(int arg0) {
			dianDian.setSelect(arg0);
		}

		@Override
		public void onPageScrolled(int arg0, float arg1, int arg2) {
		}

		@Override
		public void onPageScrollStateChanged(int arg0) {
		}
	};

好吧,我只写了一句dianDian.setSelect(arg0)。

(4)自定义切换动画

上面三步做完后ViewPager已经能正常使用了。

但是呢我们还想做点酷酷的事情,于是决定自定义ViewPager的切换动画效果。

所以百度了一下,发现Google提供了简单的方法来实现:

		viewPager.setPageTransformer(false, new PageTransformer() {

			@Override
			public void transformPage(View view, float position) {
			}
		});

其中第一个参数我们不管(好像是绘制顺序什么的),第二个参数是PageTransformer类型。

其实这里跟设置了监听一样的,就像我们经常写的:

		button.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {

			}
		});

viewPager对应button,PageTransformer对应OnClickListener。

所不同的是OnClickListener的OnClick方法会在button被点击的时候被调用,并且把button当做参数传过去。

而PageTransformer的transformPage方法则是会在ViewPager被拖动的时候被调用。

下面来分析下transformPage方法的两个参数:

view:

当ViewPager被拖动时,会有两个Pager被影响,即当前显示的pager1和拖动后导致可见的pager2。而这个view参数就是这两个中的一个。这里不像Button的点击回调一样,点击一次就回调一次onClick,而是回调两次,两次的view参数分别为pager1和pager2,所传入的第二个参数position也是不同的。

position:一张图来解释,嫌我画的丑的你来打我啊

所以呢PageTransformer只是告诉我们ViewPager当前拖动的状态,移动了百分之几而已。动画效果还需要我们自己实现,而我们需要view参数作为动画的主体,position作为动画的参数。

一个官方例子,也是效果图的实现:

		viewPager.setPageTransformer(false, new PageTransformer() {

			@Override
			public void transformPage(View view, float position) {
				int pageWidth = view.getWidth();
				if (position < -1) {
					view.setAlpha( 0);
				} else if (position <= 0) {
					view.setAlpha( 1 - position);
					view.setTranslationX( 0);
					view.setScaleX( 1);
					view.setScaleY( 1);
				} else if (position <= 1) {
					view.setAlpha(1 - position);
					view.setTranslationX(pageWidth * -position);
					float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
					view.setScaleX(scaleFactor);
					view.setScaleY(scaleFactor);
				} else {
					view.setAlpha(0);
				}
			}
		});

注意小于等于0的是对左边pager(即pager1)的处理,大于0的是对右边pager(即pager2)的处理。

通过拖动的百分比设置不同的透明度、位移、缩放等就可以实现我们的自定义动画了,读者可以自己试着实现几个。

就在这时候,问题出现了,你的IDE像个受惊的小婊砸向你呼喊,于是一条条的红线像血一样染红了夕阳。

跑偏了。。。

属性动画是在API 3.0之后才有的,所以setScaleX这些方法在11版本以下api是没有的。

你可以加一条NewApi注解忽略报错,这样的话在api11及以上版本能显示自定义的动画效果,在以下版本只能显示默认效果,貌似这样就不错了。

但是呢我却还不满足,我无法抑制我的饥渴。

于是呢我想到了大名鼎鼎的动画兼容库Nine Old Androids。

Ok,导入该库,修改调用语句为下面这种样式:

ViewHelper.setAlpha(view, 1 - position);

啊,一切都是那么美好。打开2.3的模拟器,接下来就是见证奇迹的时刻了。

开启中... ...开启中... ...开启中... ...开启中... ...

忍着把这破电脑砸掉的冲动,开始运行,然后开始拖动,然后就没有然后了。

坑爹啊这是,没起作用!!!还是默认的拖动动画!!!

不要惊慌,深呼吸。

出现问题,先查官方api说明,setPageTransformer方法的说明:

Note: Prior
to Android 3.0 the property animation APIs did not exist. As a result, setting a PageTransformer prior to Android 3.0 (API 11) will have no effect.

坑爹呢这是,你又知道我调用这个就一定是为了实现属性动画,我没事调着玩不行啊。

看下它是怎么忽略低版本这个被歧视的种族的:

	public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
		if (Build.VERSION.SDK_INT >= 11) {
			final boolean hasTransformer = transformer != null;
			final boolean needsPopulate = hasTransformer != (mPageTransformer != null);
			mPageTransformer = transformer;
			setChildrenDrawingOrderEnabledCompat(hasTransformer);
			if (hasTransformer) {
				mDrawingOrder = reverseDrawingOrder ? DRAW_ORDER_REVERSE : DRAW_ORDER_FORWARD;
			} else {
				mDrawingOrder = DRAW_ORDER_DEFAULT;
			}
			if (needsPopulate)
				populate();
		}
	}

我去,够狠,直接一个if判断,小于11直接啥事不干。

这里我们最关心的语句就是:

mPageTransformer = transformer;

将我们设置的transformer保存了下来,可以猜想,在拖动时一定会有类似代码被执行:

		if(mPageTransformer!=null){
			mPageTransformer.transformPage(view,position);
		}

所以现在我们的关键就是把我们的transformer赋值给mPageTransformer。

好,开始尝试。

首先呢要自定义一个Viewpager,并且重写setPageTransformer方法。

然后在setPageTransformer方法里加个判断,如果api低于11,我们自己处理,高于11,交给父类处理。

我们的处理就是要让语句mPageTransformer = transformer得以执行,而mPageTransformer已经有了个set方法,按照编码规范它应该是被声明为私有的了,我们无法直接获得其引用,一试果然如此。

我们只好另辟蹊径,用反射来实现了。

高清源码:

	@Override
	public void setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) {
		if (Build.VERSION.SDK_INT < 11) {
			try {
				//找到mPageTransformer变量,注意要在父类ViewPager找
				Field field = this.getClass().getSuperclass().getDeclaredField("mPageTransformer");
				//设置修改权限
				field.setAccessible(true);
				//这里相当于执行了语句mPageTransformer = transformer;
				field.set(this, transformer);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			super.setPageTransformer(reverseDrawingOrder, transformer);
		}
	}

把布局的ViewPager改成我们自定义的AnimViewPager,怀着万分期待的心情,再次运行。bingo,成功了。实际上上面的效果图就是在2.3的模拟器上截的。

因为是通过反射实现的,要是那天Google突然抽风把变量名换了,那么我们做的就失效了。当然这种事情发生的概率很低,也不用太在意。

本篇结束

Demo下载

时间: 2024-08-29 12:00:15

Android好奇宝宝_07_ViewPager切换动画(兼容低版本)的相关文章

android api实现高斯模糊,且兼容低版本

一.利用android api实现高斯模糊 关于高斯模糊网上已经有很多不错的文章介绍了,在这里就不重复了.先来看一个效果 效果已经看到了,就来看看怎么实现的吧.首先是实现高斯模糊的核心代码 <span style="white-space:pre"> </span>public static Bitmap blurBitmap(Bitmap bitmap, Context context) { // 用需要创建高斯模糊bitmap创建一个空的bitmap Bit

Android好奇宝宝_番外篇_看脸的世界_08

废话少说,先上效果图: (左侧的图片是我用window画图软件1分钟画的,所以就不要嫌丑了,You can you up no bb.) 这是我发过最挫的效果图了,不过这是由于没有图片素材导致的,就不要在意这些细节了,知道实现原理后完全可以发挥你的想象去实现更美观的效果. 这个效果也是有开源库的,不过我又把名字给忘了,不过我记得原理,于是就试着自己写了一下. 其实原理很简单,我在另一篇博客(一个有吃豆人删除动画的ListView)也说过了,这一篇当做兑换那些年少轻狂不更事时许下的诺言(是不是瞬间

Android的Activity屏幕切换动画(一)-左右滑动切换

这段时间一直在忙Android的项目,总算抽出点时间休息一下,准备把一些项目用到的Android经验分享一下. 在Android开发过程中,经常会碰到Activity之间的切换效果的问题,下面介绍一下如何实现左右滑动的切换效果,首先了解一下Activity切换的实现,从Android2.0开始在Activity增加了一个方法: public void overridePendingTransition (int enterAnim, int exitAnim) 其中: enterAnim 定义A

Android好奇宝宝_04_一个有3个功能的Adapter

感觉Android好奇宝宝这个系列是脱离不了ListView和GridView了... 这一篇呢来分享点好东西 一个自定义Adapter,可以快速实现三个功能: (1)自动缓存处理 好吧,这个功能不是我实现的.我只是照搬鸿洋大大的,我会简单说下,不过还是请先看下他的原文,再来看我添加的两个功能,传送门 (2)支持item的不同布局 提供一个接口来通过position和该position的数据来设置不同的布局 (3)局部刷新 只刷新指定item的某个子View,避免一直调用notifyDataSe

[转]AppCompat 22.1,Goole暴走,MD全面兼容低版本

AppCompat 22.1,Goole暴走,MD全面兼容低版本 分类: Android2015-04-24 09:48 1354人阅读 评论(0) 收藏 举报 android 目录(?)[+] 武侠相关的文学作品中常用“四十岁后不滞于物,草木竹石均可为剑.自此精进,渐入无剑胜有剑之境.”形容一个人的武功技艺高超,已臻化境.而我们IT工程师自身的技艺水平到达一定程度后,也会处于这种“无码胜有码”的境界.但这是什么意思呢?是说我们从此不再写代码,每天躺在床上和白花花的天花板无言对望了吗? 不是的.

GridView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL)不兼容低版本系统解决方案

项目开发中需要使用GridView批处理操作,多项选择. 但是GridView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL)不兼容低版本. 找解决方案,查看android sdk提供的demo里介绍的GridView多项选择的方法 public class Grid3 extends Activity { GridView mGrid; @Override protected void onCreate(Bundle savedInstan

兼容低版本的array_column

//兼容低版本的array_column public function array_column($input, $columnKey, $indexKey = NULL) { $columnKeyIsNumber = (is_numeric($columnKey)) ? TRUE : FALSE; $indexKeyIsNull = (is_null($indexKey)) ? TRUE : FALSE; $indexKeyIsNumber = (is_numeric($indexKey))

模拟实现兼容低版本IE浏览器的原生bind()函数功能

模拟实现兼容低版本IE浏览器的原生bind()函数功能: 代码如下: if(!Function.prototype.bind){   Function.prototype.bind=function(oThis){     if (typeof this !== 'function'){       throw new TypeError('调用者不是当前函数对象');     }       var aArgs = Array.prototype.slice.call(arguments, 1

兼容低版本IE浏览器的边框阴影效果

兼容低版本IE浏览器的边框阴影效果:使用box-show属性可以实现边框阴影效果,但是IE8和IE8以下浏览器不支持此属性,不过可以通过以下方式实现,当然实现全兼容的方式有多种,下面是一种比较简陋的方式,代码实例如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://www