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的屬性實現更多複雜和好看的動畫效果,網上也有很多開源的項目已經做到了,大家可以根據自己的需要進行設置,麼麼噠~

源碼地址

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