1.引言

ViewPager是我们比较熟悉的一个控件,用于视图切换或作为首页的切换,默认是没有动画切换效果的,当然我们可以通过setPageTransform来添加各种各样的动画,这里我们介绍另外一种方法,自定义自带动画切换效果的ViewPager。

2.准备工作

首先分析我们要实现动画切换效果,我们必须要准备的工作:

1.首先必须获取两个切换的视图,mLeftView 和 mRightView。

2.然后还必须有一个变化的梯度值,用于控制切换过程中的值变化。

新建一个类 ViewPagerWithTransformAnim.java 继承自ViewPager

public class ViewPageWithTransformAnim extends ViewPager{
	//两个View
	private View mLeft;
	private View mRight;
	//变化的梯度
	private float mTrans;
	//缩放的值
	private float mScale;
	//缩放的最小值,从0.5 到 1
	private static final float MIN_SCALE = 0.5f;
	public ViewPageWithTransformAnim(Context context) {
		super(context);
		
	}
	public ViewPageWithTransformAnim(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onPageScrolled(int position, float offset, int pix) {
		Log.i("meng.li", "position = "+position +" offset = "+offset+" pix = "+pix);
		super.onPageScrolled(position, offset, pix);
	}
}

ViewPager中有一个onPageScrolled方法,我们可以在这里获取我们需要的东西。

我们观察onPageScrolled方法的log信息:

从第0页滑动到第1页

android弹出动画View android viewpager 动画_android弹出动画View

从第1页回到第0页

android弹出动画View android viewpager 动画_android_02

总结下规律

0页到1页  position  0 ; offset  0-1

1页到0页  position 0  ; offset   1-0

下面仔细研究下这几个参数:
直接说测试结果:
在非第一页与最后一页时,滑动到下一页,position为当前页位置;滑动到上一页:position为当前页-1
positionOffset 滑动到下一页,[0,1)区间上变化;滑动到上一页:(1,0]区间上变化
positionOffsetPixels这个和positionOffset很像:滑动到下一页,[0,宽度)区间上变化;滑动到上一页:(宽度,0]区间上变化
第一页时:滑动到上一页position=0 ,其他基本为0 ;最后一页滑动到下一页 position为当前页位置,其他两个参数为0

豁然发现,我们需要的步骤的第二步解决了,positionOffset很适合作为,渐变,缩放的控制参数;positionOffsetPixels则可以作为平移等的控制参数。
那么如何获得当前View和目的View呢:
分享几个我的歧途:
1、【错误】我通过getChildAt(position),getChildAt(position+1),getChildAt(position-1)获得滑动时,左右的两个View;乍一看,还真觉得不错~~~在代码写出来,再乍效果也出不来~~错误原因:我们忽略一个特别大的东西,ViewPager的机制,滑动时动态加载和删除View,ViewPager其实只会维持2到3个View,而position的范围基本属于无限~~
2、【错误】我通过getCurrentItem获得当前的位置,然后+1,-1获得后一个或者前一个~~正在窃喜,赶快代码改过来,效果怎么也不对,乱七八糟的~~仔细观察日志,这个getCurrentItem当用户手指离开的屏幕,Page还在动画执行时,就改变了~~难怪~整个滑动过程并不是固定的~~唉,心都碎了~

android弹出动画View android viewpager 动画_android弹出动画View_03


3、【错误】position在整个滑动的过程中是不变化的,而且ViewPager会保存2个或3个View;那么我考虑,如果是第一页、或者最后一页那么我取getChildAt(0)和getChildAt(1),如果在其他页面则为getChildAt(0),getChildAt(2),然后经过一系列的变化~我想这会总该对了吧,于是我遇到第一问题,第一页的时候,不管左右position都为0,尼玛,这哪个为左View,哪个为右View~~

说了这么多错误,大家可以绕过这些弯路,也能从这些弯路里面看出点什么

说了这么多错误,大家可以绕过这些弯路,也能从这些弯路里面看出点什么~

下面说正确的,其实ViewPager在添加一个View或者销毁一个View时,是我们自己的PageAdapter中控制的,于是我们可以在ViewPager里面维系一个HashMap<Position,View>,然后滑动的时候,通过get(position)取出,比如上述效果,始终是右边的View变化,要么从小到大,要么从大到小

那么滑到下一页:左边的View:map.get(position) ,右边的View : map.get(position+1) .
那么滑到上一页:左边的View : map.get(position) , 右边的View : map.get(position+1) , 一样的,因为滑到上一页,position为当前页-1
好了,至此,我们分析了且解决了所有步骤

代码: MainActivity.java

public class MainActivity extends Activity {
//	private ViewPager viewPager;
	//这里是为了兼容3.0版本下设置动画效果从新改写的ViewPager
//	private ViewPagerCompat viewPager;
	//自定义带动画切换效果的ViewPager
	private ViewPageWithTransformAnim viewPager;
	private int[] mImgId = new int[]{R.drawable.guide_image1,R.drawable.guide_image2,R.drawable.guide_image3};
	List<ImageView> mImages = new ArrayList<ImageView>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewPager = (ViewPageWithTransformAnim)findViewById(R.id.view_pager);
        viewPager.setAdapter(new PagerAdapter() {
        	//实例化 Item 创建ImageView
			@Override
			public Object instantiateItem(ViewGroup container, int position) {
				ImageView imageView = new ImageView(MainActivity.this);
				imageView.setImageResource(mImgId[position]);
				imageView.setScaleType(ScaleType.CENTER_CROP);
				container.addView(imageView);
				mImages.add(imageView);
				//添加到View中
				viewPager.setViewForPosition(imageView,position);
				return imageView;
			}
			@Override
			public boolean isViewFromObject(View arg0, Object arg1) {
				//判断view是否来自于 arg1 arg1 是初始化的时候添加进去的
				return arg0 == arg1;
			}
			
			@Override
			public int getCount() {
				//获取视图的长度
				return mImgId.length;
			}
			@Override
			public void destroyItem(ViewGroup container, int position,
					Object object) {
				//移除视图
				viewPager.removeViewFromPosition(position);
				//移除视图
				container.removeView(mImages.get(position));
			}
		});
        
    }
}

这个很常见的代码,就是初始化ViewPager~~就没啥可说的了~~有一点需要注意:在instantiateItem方法,我们多调用了一个mViewPager.setObjectForPosition(imageView, position);其实就是为了给我们的Map存值

自定义ViewPager

/*
 * 自定义的带动画切换效果的ViewPager
 */
/*需要有哪些准备
 * a.需要拿到当前切换的两个 View
 * b.需要拿到一个动画的梯度值
 * Translation 和 Scale
 */
public class ViewPageWithTransformAnim extends ViewPager{
	private Map<Integer, View>  vChildren= new HashMap<Integer, View>();
	public void setViewForPosition(View view,int position){
		vChildren.put(position, view);
	}
	public void removeViewFromPosition(int position){
		vChildren.remove(position);
	}
	//两个View
	private View mLeft;
	private View mRight;
	//变化的梯度
	private float mTrans;
	//缩放的值
	private float mScale;
	//缩放的最小值,从0.5 到 1
	private static final float MIN_SCALE = 0.5f;
	public ViewPageWithTransformAnim(Context context) {
		super(context);
		
	}
	public ViewPageWithTransformAnim(Context context, AttributeSet attrs) {
		super(context, attrs);
	}
	@Override
	protected void onPageScrolled(int position, float offset, int pix) {
//		Log.i("meng.li", "position = "+position +" offset = "+offset+" pix = "+pix);
		mLeft = vChildren.get(position);
		mRight = vChildren.get(position+1);
		//添加动画效果
		animStack(mLeft,mRight,offset,pix);
		Log.i("meng.li", getCurrentItem() +" -- "+getChildCount());
		super.onPageScrolled(position, offset, pix);
	}
	private void animStack(View left,View right,float offset,int offsetPixels){
		if(right != null){
			//从0页到1页, offset: 0-1
			//从0 逐渐变到1
			mScale = (1 - MIN_SCALE)* offset + MIN_SCALE;
			//从 0 到宽度的变化
			mTrans = -getWidth() -getPageMargin() + offsetPixels;
			
			ViewHelper.setScaleX(right, mScale);
			ViewHelper.setScaleY(right, mScale);
			ViewHelper.setTranslationX(right, mTrans);
		}
		if(left != null){
			//让其保持在顶部绘制
			left.bringToFront();
		}
	}

}

可以看到,核心代码都是onPageScrolled,我们通过findViewFromObject(position); findViewFromObject(position + 1);分别获取了左右两边的View,然后添加动画效果;当前这个例子添加了两个动画,一个是从0.5放大到1.0或者1.0缩小到0.5,没错由我们的positionOffset提供梯度的变化~~还有个平移的动画:下一页直接移动到当前屏幕(默认是在右边,可以注释这个效果,怎么运行看看),然后不断的通过positionOffsetPixels抵消原来默认移动时的位移,让用户感觉它就在原地放大缩小~~

布局文件

<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"
    tools:context="com.example.viewpageranimation.MainActivity" >
    <com.example.viewpageranimation.ViewPageWithTransformAnim
         android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>

实现的效果为:

android弹出动画View android viewpager 动画_android弹出动画View_04

4、JazzyViewPager的使用
其实上面的实现就是github上JazzyViewPager的源码,用法不用说了,就是我们的MainActivity,它内置了大概10来种效果,我们可以通过代码或者布局上面设置动画效果~~我们上面的例子效果,它叫做Stack;
使用JazzViewPager的代码:其实基本一样~~最后也会贴上JazzyViewPager的源码的下载

MainActivity

package com.jfeinstein.jazzyviewpager;  
  
import com.jfeinstein.jazzyviewpager.JazzyViewPager.TransitionEffect;  
  
import android.app.Activity;  
import android.os.Bundle;  
import android.support.v4.view.PagerAdapter;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.ImageView;  
import android.widget.ImageView.ScaleType;  
  
public class MainActivity extends Activity  
{  
    protected static final String TAG = "MainActivity";  
    private int[] mImgIds;  
    private JazzyViewPager mViewPager;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        mImgIds = new int[] { R.drawable.a, R.drawable.b, R.drawable.c,  
                R.drawable.d };  
        mViewPager = (JazzyViewPager) findViewById(R.id.id_viewPager);  
        //设置切换效果  
        mViewPager.setTransitionEffect(TransitionEffect.Stack);  
          
          
        mViewPager.setAdapter(new PagerAdapter()  
        {  
  
            @Override  
            public boolean isViewFromObject(View arg0, Object arg1)  
            {  
                return arg0 == arg1;  
            }  
  
            @Override  
            public void destroyItem(ViewGroup container, int position,  
                    Object object)  
            {  
                container.removeView((View) object);  
            }  
  
            @Override  
            public Object instantiateItem(ViewGroup container, int position)  
            {  
                ImageView imageView = new ImageView(MainActivity.this);  
                imageView.setImageResource(mImgIds[position]);  
                imageView.setScaleType(ScaleType.CENTER_CROP);  
                container.addView(imageView);  
                mViewPager.setObjectForPosition(imageView, position);  
                return imageView;  
            }  
  
            @Override  
            public int getCount()  
            {  
                return mImgIds.length;  
            }  
        });  
  
    }  
  
}

与我们的代码唯一区别就是:

//设置切换效果

mViewPager.setTransitionEffect(TransitionEffect.Stack);

它有12中可选的切换效果,其实就是写了12个切换的动画~~~

好了,最后附上一个我比较喜欢的效果:Tablet

android弹出动画View android viewpager 动画_ViewPager动画切换_05