片段共享元素转换无法与ViewPager

时间:2015-06-03 19:05:09

标签: android android-viewpager android-transitions

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

以下是一个非常简单的视频:

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

这只是使用片段 - >片段转换。

将起始片段放在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团队的疏忽吗?有什么办法可以解决这个问题吗?感谢。

3 个答案:

答案 0 :(得分:36)

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

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

  • 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。使用相同的转换名称修复多个片段的问题:

现在有很多方法可以做到这一点,但这是我在“详细”活动中结束的内容,即包含ViewPager的活动(在onCreate()但是你可以在任何地方做到这一点:

_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. 在可见片段上设置转换名称。
  5. 请致电finish()
  6. ,而不是致电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

    就我而言,我没有必要实施他的解决方案以及我发布的上述解决方案。

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

答案 1 :(得分:1)

关于活动 supportPostponeEnterTransition();

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

startPostponedEnterTransition();

请参阅此示例

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

答案 2 :(得分:0)

我最近一直用这个撞到墙上。我想要的只是ViewPager中的一个片段,用于启动另一个具有良好扩展卡片类型共享元素转换的片段。上面没有任何建议对我有用,所以我决定尝试启动一个类似对话框的活动:

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

然后从片段中启动活动:

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 =&#34; target_transition&#34; (与Pair.create()第二个参数相同)。