Android动画

自定义ViewPager切换动画

2017-02-26  本文已影响170人  陌上疏影凉

实现基础的ViewPager###

1.主布局文件activity_main.xml,一个用于放ViewPager的RelativeLayout容器,里面就是我们用于展示的ViewPager了。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg"
    tools:context="com.momo.pagetransformerdemo.activity.MainActivity">

    <RelativeLayout
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:clipChildren="false"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="50dp">

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:clipChildren="false"
            android:layout_centerHorizontal="true">
        </android.support.v4.view.ViewPager>

    </RelativeLayout>
</RelativeLayout>

这里有个需要注意的地方:RelativeLayout和ViewPager的clipChildren属性都需要设置为false,目的是让ViewPager左边右边的界面也显示出来,不然ViewPager只会显示中间ViewPager在布局上所占的那部分空间。如果不清楚直接看下图:

clipChildren=“false” clipChildren=“true”

2.ViewPager的Adapter类,在构造函数中传入要显示的数据源,这里就是每个page上要显示的文本。在instantiateItem()方法中加载要显示的页面,找到其中的TextView,设置要显示的文本。至于这四个函数的作用可以参考ViewPager 详解(二)---详解四大函数

MyPagerAdapter.java

public class MyPagerAdapter extends PagerAdapter {
    private String[] mDatas;
    private Context mContext;

    public MyPagerAdapter(Context context,String[] mDatas) {
        this.mContext =context;
        this.mDatas = mDatas;
    }

    @Override
    public int getCount() {
        return mDatas.length;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.view_pager_item,null);
        TextView tv = (TextView) view.findViewById(R.id.tv_text);
        tv.setText(mDatas[position]);
        container.addView(view);
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        View view = (View) object;
        container.removeView(view);
    }

3.ViewPager的每个界面布局view_pager_item.xml,就是一个Textview,没啥好说的。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/item_bg">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Page 1"
        android:textSize="20dp"
        android:layout_centerInParent="true"
        android:textColor="#ffffff"
        />
</RelativeLayout>

4.MainActivity.java

public class MainActivity extends AppCompatActivity {

    private RelativeLayout rl_container;
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findId();
        initViewPager();
    }

    private void findId() {
        viewPager = (ViewPager) findViewById(R.id.view_pager);
        rl_container = (RelativeLayout) findViewById(R.id.rl_container);
    }

    private void initViewPager() {
        //创建数据源
        String[] stringArr = new String[8];
        for(int i=0;i<stringArr.length;i++){
            stringArr[i] = "Page" + String.valueOf(i);
        }
        //设置Adapter
        viewPager.setAdapter(new MyPagerAdapter(this,stringArr));
        //设置缓存页面数量
        viewPager.setOffscreenPageLimit(stringArr.length/2);
        //设置页面间距
        viewPager.setPageMargin(10);
    }
}

写到这里我们可以来运行一下,结果发现只有中间滑动有效果,而如果在两边滑动时,ViewPager是不会切换页面的。这是因为点击事件被容器RelativeLayout拦截了。加入下面的代码就能解决问题了。

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        initViewPager();
        
        rl_container.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return viewPager.dispatchTouchEvent(event);
            }
        });
    }

给RelativeLayout设置OnTouchListener使得当其接收到触摸事件时会将该事件转给ViewPager进行分发。这样就能让ViewPager处理左右两边的触摸事件了。

至此,一个简单的ViewPager就完成了,来看看效果。

PageTransformer###

这个类是我们实现自定义切换动画的核心。ViewPager有一个setPageTransformer()方法用于设置切换动画。goolgle官方提供了两个动画效果,DepthPageTransformer和ZoomOutPageTransformer,具体效果可以参考一下鸿洋大神的这篇博客Android 实现个性的ViewPager切换动画 实战PageTransformer(兼容Android3.0以下)。关于下面要讲到的position的意义,如果觉得博主讲的不清楚也可以参考这篇博客Android 自定义 ViewPager 打造千变万化的图片切换效果

我们看到PageTransformer中要实现这样一个函数:

@Override
public void transformPage(View page, float position) {

 }  

解释一下,这里的page指的就是在滑动的页面,而position是指该页面的位置,用下面这张图解释:

2017-02-26_195739.png

position取值可以看成该页面左上角的位置

举个栗子:


上图中page0的position为-2,page1为-1,page2为当前显示页面,position为0,page3为1,page4为2。在page2往左边划的过程中,page2的position值会从1变到0,同理,page3的position值会从1变到0。

有了这样的一个position值,便可以开始制作动画了。
制作动画前先看一下要用到的几个函数(这里可以参考这篇博客【Android开发】View的平移、缩放、旋转以及位置、坐标系)


锚点的位置,将决定page缩放后所在的位置。因为默认是中心坐标,所以缩放后,其结果在水平中心位置或垂直中心位置。但是,如果锚点的位置变了,那么View缩放后的位置也将发生变化。
旋转时,将会以锚点所在的轴线为轴进行旋转,比如:

2017-02-26_205747.png 注意两张page0的区别

了解以上几个函数的效果就可以动手写动画了,所谓动画无非就是像总结一个函数一样,对于任意的position能找到一个特定的属性与之对应,这个值可以是透明度、旋转角度、位移等等。
举个栗子,如果我们想实现下面这种动画,只要找出这样一个对应的函数就可以了。

** 总结规律:**

我们还可以根据上面的规律画成图像:


translationY对应的函数图像

有了这样清晰的概念写出动画就不是什么难事了。下面看源码:

@Override
public void transformPage(View page, float position) {
    page.setPivotY(page.getHeight()/2);
    float maxTransform = page.getHeight()/4;
    if (position <= -1)
    { // [-Infinity,-1)
        // This page is way off-screen to the left.
        page.setTranslationY(maxTransform);
    } else if (position < 1)
    { // [-1,1]
        // Modify the default slide transition to shrink the page as well
        if (position < 0)//[0,-1]
        {
            page.setTranslationY(-position * maxTransform);
        } else//[1,0]
        {
            page.setTranslationY(position * maxTransform);
        }
    } else
    { // (1,+Infinity]
        // This page is way off-screen to the right.
        page.setTranslationY(maxTransform);
    }
}

再看个复杂点的

@Override
public void transformPage(View page, float position) {
    page.setPivotY(page.getHeight()/2);
    float maxRotate = 35f;
    float minScale = 0.8f;
    float maxTranslationX = page.getWidth()/5;
    if (position <= -1)
    { // [-Infinity,-1)
        // This page is way off-screen to the left.
        page.setRotationY(maxRotate);
        page.setPivotX(0);
        page.setScaleX(minScale);
        page.setScaleY(minScale);
        page.setTranslationX(maxTranslationX);
    } else if (position < 1)
    { // [-1,1]
        page.setRotationY(-position * maxRotate);
        if (position < 0)//[0,-1]
        {
            page.setPivotX(0);
            page.setScaleX(1 + position * (1-minScale));
            page.setScaleY(1 + position * (1-minScale));
            page.setTranslationX(-position * maxTranslationX);
        } else//[1,0]
        {
            page.setPivotX(page.getWidth());
            page.setScaleX(1 - position * (1-minScale));
            page.setScaleY(1 - position * (1-minScale));
            page.setTranslationX(-position * maxTranslationX);
        }
    } else
    { // (1,+Infinity]
        // This page is way off-screen to the right.
        page.setRotationY(-1 * maxRotate);
        page.setPivotX(page.getWidth());
        page.setScaleX(minScale);
        page.setScaleY(minScale);
        page.setTranslationX(-maxTranslationX);
    }
}

最后啰嗦两句###

总算写完了,第一次写博客,找各种各样的工具花了好长时间,录屏使用的是手机app录屏专家,再传到电脑上使用Free Video to GIF Converter转成gif,断断续续花了一晚上。如果大家有好的录屏软件推荐麻烦给博主留个言,谢谢了!

上一篇下一篇

猜你喜欢

热点阅读