当前位置: 代码迷 >> Android >> Android猎奇宝宝_07_ViewPager切换动画(兼容低版本)
  详细解决方案

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

热度:43   发布时间:2016-04-28 02:53:56.0
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下载

  相关解决方案