Android实现ViewPager视差动画效果及背景渐变过渡

在上一篇文章(TabLayout与ViewPager配合使用踩坑总结)中我记录了TabLayout搭配ViewPager使用过程中的踩坑过程,那么这一篇文章我来写一下ViewPager切换的视差动画效果及背景渐变过渡是如何实现的!

一、视差动画实现

惯例首先来看看ViewPager的切换效果:
在这里插入图片描述
注意下方的三行文字,在切换的时候是有一个视觉差的,也就是它们仨不是以同一个偏移量移动,可以看到名称那一行在滑动时偏移量比其他的大,第二行比第三行偏移量大,这就是我们说的视差效果,那么如何实现呢?

在上一篇的工程源码中可以看到,在PagerAdapter的实现中,instantiateItem方法最后我用一个数组mLayoutViewIdsMap将当前item布局中所有的id数组记录下来了:

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
  View view = LayoutInflater.from(MainActivity.this)
      .inflate(R.layout.viewpager_item, null, false);
  GirlBean bean = girlList.get(position);
  if (null == bean) {
    return null;
  }
  TextView tvName = view.findViewById(R.id.tv_name);
  tvName.setText(bean.name);
  TextView tvDesc = view.findViewById(R.id.tv_desc);
  tvDesc.setText(bean.desc);
  TextView tvLike = view.findViewById(R.id.tv_like);
  tvLike.setText(bean.like);
  //存储布局ID
  int[] ids = new int[] {
      R.id.tv_name, R.id.tv_desc, R.id.tv_like
  };
  mLayoutViewIdsMap.put(position, ids);
  container.addView(view);
  return view;
}

接下来就是使用ViewPager的setPageTransformer方法,这个方法可以给ViewPager设置切换场景的动画效果,我们这里只列出这个项目中用到的,就是每个Item中各个控件的偏移视差效果动画。

新建一个类实现PageTransformer:

private class ParallaxTransformer implements ViewPager.PageTransformer {

  float parallaxCoefficient;
  float distanceCoefficient;

  public ParallaxTransformer(float parallaxCoefficient, float distanceCoefficient) {
    this.parallaxCoefficient = parallaxCoefficient;
    this.distanceCoefficient = distanceCoefficient;
  }

  @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  @Override
  public void transformPage(View page, float position) {
    float scrollXOffset = page.getWidth() * parallaxCoefficient;

    ViewGroup pageViewWrapper = (ViewGroup) page;
    @SuppressWarnings("SuspiciousMethodCalls")
    int[] layer = mLayoutViewIdsMap.get(viewPager.getCurrentItem());
    for (int id : layer) {
      View view = page.findViewById(id);
      if (view != null) {
        view.setTranslationX(scrollXOffset * position);
      }
      scrollXOffset *= distanceCoefficient;
    }
  }
}

transformPage方法中两个参数,page表示当前滑动的ViewPager中的Item,position表示滑动的位置,比如0->-1,1->0,具体的可以打log观察。

这里我们主要是取出每个position对应的Item的各个控件,然后对它设置一个setTranslationX偏移量,因为它们自身在布局中的位置不一样,所以这里通过view.setTranslationX(scrollXOffset * position)运行的效果也不一样,所以这样就实现了我们说的视差效果,如果有需要我们还可以对控件设置透明度等等。

最后调用

viewPager.setPageTransformer(true,
        new ParallaxTransformer(PARALLAX_COEFFICIENT, DISTANCE_COEFFICIENT));

设置给ViewPager即可。

二、背景图片切换过渡

在这里插入图片描述
可以看出在切换过程中,背景图片是有一个渐变的效果的,这个背景位置因为是静态的,所以不能跟随ViewPager一起左右切换,所以图片就不适合放到PagerAdapter的Item布局中,应该放到ViewPager平级的布局文件中,作为一个底图背景:

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

  <ImageView
    android:id="@+id/bg1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:background="@drawable/girl1" />

  <ImageView
    android:id="@+id/bg2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:background="@drawable/girl2" />

  <ImageView
    android:id="@+id/bg3"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="centerCrop"
    android:background="@drawable/girl3" />

  <android.support.v4.view.ViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

  <com.zhangyan.mytablayout.tablayout.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:tabGravity="fill"
    app:tabIndicatorColor="#CBA788"
    app:tabIndicatorHeight="3dp"
    app:tabLineOffset="10dp"
    app:tabMode="fixed"
    app:tabSelectedTextColor="#CBA788"
    app:tabTextColor="#CCFFFFFF" />

</RelativeLayout>

这里我放了三个ImageView作为背景。

bgImg1 = findViewById(R.id.bg1);
bgImg2 = findViewById(R.id.bg2);
bgImg3 = findViewById(R.id.bg3);

//刚进来只显示第一张背景
bgImg2.getBackground().setAlpha(0);
bgImg3.getBackground().setAlpha(0);

初始化的时候先将后两张透明度设为0,这样只显示出第一张。

剩下的处理逻辑就需要依赖OnPageChangeListener中实现了,实现OnPageChangeListener这个类:

private ViewPager.OnPageChangeListener mOnPageChangeListener =
      new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    float fraction = positionOffset;
    if (position == 0) {
      bgImg1.getBackground().setAlpha(255);
      bgImg2.getBackground().setAlpha((int) (fraction * 255));
      bgImg3.getBackground().setAlpha(0);
    }
    if (position == 1) {
      bgImg2.getBackground().setAlpha(255);
      bgImg3.getBackground().setAlpha((int) (fraction * 255));
      bgImg1.getBackground().setAlpha(0);
    }
    if (position == 2) {
      bgImg3.getBackground().setAlpha(255);
    }
  }

  @Override
  public void onPageSelected(int position) {

  }

  @Override
  public void onPageScrollStateChanged(int state) {

  }
};

在onPageScrolled这个方法中有三个参数:

  • position:当前滑动pager的position,第一张到第二张position从0开始,完全滑动到第二张时,position=1;第二张到第三张position从1开始,完全滑动到第三张时,position=2;以此类推。
  • positionOffset:滑动的比例,往下一张滑动时,positionOffset从0慢慢过渡到1,往上一张滑动时,positionOffset从1慢慢过渡到0;
  • positionOffsetPixels:滑动的像素,从0开始,最大值为屏幕的宽度(分辨率)

我们这里主要是在onPageScrolled中对三张图的背景alpha进行变化,实现我们所需要的效果即可,其他的逻辑大家可以尝试着改改。

三、最后

我们可以依托ViewPager的属性实现更多复杂和好看的动画效果,网上也有很多开源的项目已经做到了,大家可以根据自己的需要进行设置,么么哒~

源码地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章