Android 带有 PageTransformer 和 SceneTransition 的 ViewPager

Android ViewPager with PageTransformer and SceneTransition

感谢您的光临。

我一直在使用 PageTransformerTransitionManager 的组合时遇到奇怪的行为。

我使用的层次结构相当简单:viewPager,每个页面都是 fragment。该片段有两种不同的布局,随 TransitionManager.go().

而变化

问题: 如果我只是滚动浏览 viewPager,一切都很好,我的 pageTransformer 应用正确的值,创建所需的视差效果。 如果我只是来回单击以更改页面内的场景,我也会得到所需的输出。

然而,每当我使用 TransitionManager.go()(让我们说两次,回到第一个布局)然后开始滚动我的 viewPager 时,视差效果就不会再出现了。

我的问题: 同时使用 PageTransformerTransitionManager 是否有任何我无法找到的已知问题?

我的代码:

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>

我的适配器相当简单:

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();
  }
}

然后是我的 PageTransformer,它独立移动视图的每个子视图

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;
    }
  }
}

最后是我的 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;
    }
  }
}

最后,这是一张显示所发生情况的 gif。可以看到,开头的"Item 1"有视差效果。来回切换场景后,PageTransformer 将不再适用。

提前致谢!

我会回答我自己的问题,以防有人遇到与我相同的问题。 问题来自我在片段中使用的 rootLayout 不是 "the right one",因此 TransitionManager 在返回第一个场景时添加了一个额外的层。

这是我更改的内容:

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);
}

由于我在我的层次结构中添加了另一层,我还必须稍微更改我的 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;
        }
    }
}