56

我的应用程序包含一个视图,该视图由一个 ViewPager 组成,该 ViewPager 由一些片段组成。当您单击这些片段之一中的项目时,预期的行为是共享元素(在本例中为图像)转换到显示有关单击内容的更多信息的片段。

这是一个非常简单的视频,它应该是什么样子:

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-114842.mp4

这只是使用 Fragment->Fragment 过渡。

当您将起始片段放在 ViewPager 中时,就会出现问题。我怀疑这是因为 ViewPager 使用其父片段的子片段管理器,这与处理片段事务的活动的片段管理器不同。这是发生了什么的视频:

https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-120029.mp4

正如我上面解释的那样,我很确定这里的问题是子片段管理器与活动的片段管理器。以下是我进行过渡的方式:

SimpleFragment fragment = new SimpleFragment();

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.am_list_pane, fragment, fragment.getClass().getSimpleName());

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    TransitionSet enterTransition = new TransitionSet();
    enterTransition.addTransition(new ChangeBounds());
    enterTransition.addTransition(new ChangeClipBounds());
    enterTransition.addTransition(new ChangeImageTransform());
    enterTransition.addTransition(new ChangeTransform());

    TransitionSet returnTransition = new TransitionSet();
    returnTransition.addTransition(new ChangeBounds());
    returnTransition.addTransition(new ChangeClipBounds());
    returnTransition.addTransition(new ChangeImageTransform());
    returnTransition.addTransition(new ChangeTransform());

    fragment.setSharedElementEnterTransition(enterTransition);
    fragment.setSharedElementReturnTransition(returnTransition);

    transaction.addSharedElement(iv, iv.getTransitionName());
}

transaction.addToBackStack(fragment.getClass().getName());

transaction.commit();

当两个片段都由活动的片段管理器管理时,这很好用,但是当我像这样加载 ViewPager 时:

ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
pager.setAdapter(new Adapter(getChildFragmentManager()));

ViewPager 的子级不再由活动管理,它不再工作。

这是 Android 团队的疏忽吗?有什么办法可以解决这个问题吗?谢谢。

4

3 回答 3

39

可能您已经找到了答案,但如果您还没有,这就是我在挠头几个小时后所做的修复它。

我认为这个问题是两个因素的结合:

  • Fragments加载有延迟,这ViewPager意味着活动返回的速度比包含在ViewPager

  • 如果你和我一样,你ViewPager的子片段很可能是同一类型。这意味着,它们都共享相同的转换名称(如果您已在 xml 布局中设置它们),除非您在代码中设置它们并且仅在可见片段上设置一次

为了解决这两个问题,这就是我所做的:

1.修复延迟加载问题:

在您的活动(包含 的活动)中,在 之后和之前ViewPager添加这一行:super.onCreate()setContentView()

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    ActivityCompat.postponeEnterTransition(this); // This is the line you need to add

    setContentView(R.layout.feeds_content_list_activity);
    ...
}

2.修复多个fragment同名的问题:

现在有很多方法可以做到这一点,但这就是我在“详细”活动中最终得到的结果,即包含的活动ViewPageronCreate()但你可以在任何地方真正做到):

_viewPager.setAdapter(_sectionsPagerAdapter);
_viewPager.setCurrentItem(position);
...
...
_pagerAdapter.getItem(position).setTransitionName(getResources().getString(R.string.transition_contenet_topic));

您需要小心,因为Activity可能尚未附加到您的ViewPager片段,因此如果您从资源加载它,只需将过渡名称从活动传递到片段就更容易了

实际的实现和你期望的一样简单:

public void setTransitionName(String transitionName) {
    _transitionName = transitionName;
}

然后在您的片段中onViewCreated(),添加以下行:

public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    ...
    if (_transitionName != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        setTransitionNameLollipop();
    }
    ...
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setTransitionNamesLollipop() {
    _imgTopic.setTransitionName(_transitionName);
}

最后一块拼图是找出你的片段何时完全加载,然后调用ActivityCompat.startPostponedEnterTransition(getActivity());.

就我而言,我的片段直到后来才完全加载,因为我从 UI 线程中加载了大部分内容,这意味着我必须想办法在加载所有内容时调用它,但如果不是你的情况,你可以调用它在你打电话后setTransitionNameLollipop()

这种方法的问题是退出转换可能不起作用,除非您非常小心并exit在活动导航之前重置“可见”片段上的转换名称。这可以像这样轻松完成:

  1. 收听您的页面更改ViewPager
  2. 一旦您的片段不在视野范围内,请立即删除过渡名称
  3. 在可见片段上设置过渡名称。
  4. 而不是打电话finish(),打电话ActivityCompat.finishAfterTransition(activity);

RecyclerView如果您返回转换需要转换到我的情况,这很快就会变得非常复杂。为此,@Alex Lockwood 在这里提供了一个更好的答案:ViewPager Fragments – Shared Element Transitions,它有一个写得很好的示例代码(尽管比我刚刚写的要复杂得多):https ://github.com /alexjlockwood/activity-transitions/tree/master/app/src/main/java/com/alexjlockwood/activity/transitions

就我而言,我不必去实施他的解决方案,而我发布的上述解决方案适用于我的案例。

如果您有多个共享元素,我相信您可以弄清楚如何扩展方法以迎合它们。

于 2015-06-29T19:13:02.793 回答
1

关于活动 supportPostponeEnterTransition();

当你的片段被加载时(尝试同步它们,可能使用 EventBus 或其他)

startPostponedEnterTransition();

参考这个样本

http://www.androiddesignpatterns.com/2015/03/activity-postponed-shared-element-transitions-part3b.html

于 2015-06-21T19:34:40.413 回答
0

我最近一直用这个把头撞在墙上。我想要的只是 ViewPager 中的 Fragment 以启动另一个具有良好扩展卡类型共享元素转换的 Fragment。上面的建议都不适合我,所以我决定尝试启动一个类似对话框的 Activity:

<style name="AppTheme.CustomDialog" parent="MyTheme">
    <item name="android:windowIsTranslucent">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowNoTitle">true</item>
    <item name="android:backgroundDimEnabled">true</item>
</style>

然后从 Fragment 启动 Activity:

Intent intent = new Intent(getContext(), MyDialogActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
                        .makeSceneTransitionAnimation(getActivity(),
                                Pair.create(sharedView, "target_transition"));
ActivityCompat.startActivity(getActivity(), intent, options.toBundle());

在 Fragment 布局中,在 sharedView 上放置一个 android:transition_name ,在 Activity 布局中具有 android:transition_name="target_transition" (与 Pair.create() 第二个参数相同)。

于 2016-09-07T09:39:44.440 回答