Search code examples
androidtransitionandroid-pagetransformer

Android ViewPager with PageTransformer and SceneTransition


and thank you for stopping by.

I've been facing a weird behavior using the combination of both PageTransformer and TransitionManager.

The hierarchy I'm using is fairly easy : A viewPager with each page being a fragment. The fragment has two different layouts, changing with the TransitionManager.go().

The issue : If I just scroll through the viewPager, everything is fine, and my pageTransformer applies the right values, creating the desired parallax effect. If I just click back and forth to change scenes inside a page, I also get the desired output.

However, whenever I use the TransitionManager.go() (let's say twice, to go back to the first layout) and then start scrolling through my viewPager, the parallax effect doesn't occur anymore.

My question : Is there any known issue I haven't been able to find with using both a PageTransformer and a TransitionManager at the same time?

My code :

Fragment1.java

public class Fragment1 extends Fragment {
private Scene                       mStartScene;
private Scene                       mInfoScene;
private Transition                  mTransition;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.view_pager_item_default, container, false);

    RelativeLayout rootLayout = (RelativeLayout) v.findViewById(R.id.p1);

    mTransition = new ChangeBounds();
    mTransition.setDuration(400);

    mStartScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_default, getContext());
    mInfoScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_details, getContext());

    return (v);
}

  public void changeScene(View v) {
    Scene tmp = mInfoScene;
    mInfoScene = mStartScene;
    mStartScene = tmp;
    TransitionManager.go(mStartScene, mTransition);
  }
}

view_pager_item_default.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/p1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
    android:id="@+id/textView"
    android:elevation="20dp"
    android:text="Item 1"
    android:onClick="changeScene"
    android:layout_marginLeft="20dp"
    android:layout_marginRight="20dp"
    android:layout_marginBottom="60dp"
    android:layout_width="match_parent"
    android:layout_alignParentBottom="true"
    android:padding="20dp"
    android:layout_height="wrap_content"
    android:background="@drawable/white_shape"
    android:textSize="40sp"
    android:gravity="center"
    android:textColor="#000000"/>
<ImageView
    android:id="@+id/imageView"
    android:elevation="19dp"
    android:scaleType="centerCrop"
    android:layout_marginBottom="-10dp"
    android:layout_centerHorizontal="true"
    android:src="@mipmap/big_image"
    android:background="@android:color/transparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</RelativeLayout>

view_pager_item_details.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/p1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
    android:id="@+id/textView"
    android:elevation="20dp"
    android:text="Item 1 description"
    android:onClick="changeScene"
    android:layout_marginTop="150dp"
    android:layout_marginLeft="20dp"
    android:layout_marginRight="20dp"
    android:layout_marginBottom="20dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/white_shape"
    android:clickable="true"
    android:textSize="30sp"
    android:gravity="center"
    android:textColor="#000000"/>
<ImageView
    android:id="@+id/imageView"
    android:elevation="21dp"
    android:layout_marginTop="50dp"
    android:layout_centerHorizontal="true"
    android:src="@mipmap/small_image"
    android:background="@android:color/transparent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
</RelativeLayout>

My adapter is fairly simple :

public class MyPagerAdapter extends FragmentPagerAdapter {
  private final List                  fragments;

  public MyPagerAdapter(FragmentManager fm, List fragments) {
    super(fm);
    this.fragments = fragments;
  }

  @Override
  public Fragment getItem(int position) {
    return (Fragment) this.fragments.get(position);
  }

  @Override
  public int getCount() {
    return this.fragments.size();
  }
}

Then is my PageTransformer which moves each children of the view independently

public class MyPagerTransformer implements ViewPager.PageTransformer {
  private float                           mParallaxCoeff;
  private float                           mDistanceCoeff;

  public MyPagerTransformer(float parallax, float distance) {
    mParallaxCoeff = parallax;
    mDistanceCoeff = distance;
  }

  @Override
  public void transformPage(View page, float position) {
    float coefficient = page.getWidth() * mParallaxCoeff;
    ViewGroup vG = (ViewGroup) page;
    for (int i = vG.getChildCount() - 1; i >= 0; --i) {
        View v = vG.getChildAt(i);
        if (v != null) {
            v.setTranslationX(coefficient * (position * position * position));
        }
        coefficient *= mDistanceCoeff;
    }
  }
}

And lastly my Activity :

public class MainActivity extends AppCompatActivity {
  private Fragment1               mFrag1;
  private Fragment1               mFrag2;
  private Fragment1               mFrag3;

  private ViewPager               mViewPager;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.content_main);
    mViewPager = (ViewPager) findViewById(R.id.main_view_pager);

    CirclePageIndicator indicator = (CirclePageIndicator) findViewById(R.id.indicator);

    if (mViewPager != null) {
        mViewPager.setPageTransformer(true, new MyPagerTransformer(8.0f, 8.0f));

        //vp.addOnPageChangeListener(listener);
        List<Fragment> fragments = new ArrayList<>();

        mFrag1 = new Fragment1();
        mFrag2 = new Fragment1();
        mFrag3 = new Fragment1();
        fragments.add(mFrag1);
        fragments.add(mFrag2);
        fragments.add(mFrag3);

        PagerAdapter realViewPagerAdapter = new MyPagerAdapter(super.getSupportFragmentManager(), fragments);

        mViewPager.setAdapter(realViewPagerAdapter);
        indicator.setViewPager(mViewPager);
    }
  }

  public void changeScene(View v) {
    switch (mViewPager.getCurrentItem()) {
        case 0:
            mFrag1.changeScene(v);
            break;
        case 1:
            mFrag2.changeScene(v);
            break;
        case 2:
            mFrag3.changeScene(v);
            break;
        default:
            break;
    }
  }
}

Lastly, here's a gif showing what happens. As you can see, at the beginning "Item 1" has the parallax effect. After switching scenes back and forth, the PageTransformer won't apply anymore.

Thanks in advance !

Issue in action


Solution

  • I will answer my own question in case anyone would bump into the same issue as I did. The problem came from the rootLayout I was using in the fragment not being "the right one", therefore the TransitionManager was adding an extra layer when going back to the first scene.

    Here's what I changed :

    Fragment1.java

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      //view_pager_root.xml is a simple empty FrameLayout
      View v = inflater.inflate(R.layout.view_pager_root, container, false); 
    
      RelativeLayout rootLayout = (RelativeLayout) v.findViewById(R.id.p1);
    
      mTransition = new ChangeBounds();
      mTransition.setDuration(400);
    
      mStartScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_default, getContext());
      mInfoScene = Scene.getSceneForLayout(rootLayout, R.layout.view_pager_item_details, getContext());
    
      return (v);
    }
    

    Since I added another layer into my hierarchy, I also had to change slightly my PageTransformer :

    @Override
    public void transformPage(View page, float position) {
        float coefficient = page.getWidth() * mParallaxCoeff;
        //vG is the FrameLayout
        ViewGroup vG = (ViewGroup) page;
        if (vG.getChildAt(0) instanceof ViewGroup) {
            //vG is now the RelativeLayout from the scene
            vG = (ViewGroup) vG.getChildAt(0);
            for (int i = vG.getChildCount() - 1; i >= 0; --i) {
                View v = vG.getChildAt(i);
                if (v != null) {
                    v.setTranslationX(coefficient * (position * position * position));
                }
                coefficient *= mDistanceCoeff;
            }
        }
    }