片段共享元素转换不适用于 ViewPager
Fragment shared element transitions don't work with ViewPager
我的应用程序包含一个视图,该视图由一个包含少数片段的 ViewPager 组成。当您单击其中一个片段中的某个项目时,预期的行为是共享元素(在本例中为图像)过渡到显示有关所单击内容的更多信息的片段。
这是一个非常简单的视频,它应该是什么样子:
https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-114842.mp4
这只是使用 Fragment->Fragment 转换。
当您将起始片段放在 ViewPager 中时会出现问题。我怀疑这是因为 ViewPager 使用其父片段的子片段管理器,这与处理片段事务的 activity 的片段管理器不同。这是发生了什么的视频:
https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-120029.mp4
我很确定这里的问题正如我上面解释的那样是子片段管理器与 activity 的片段管理器。以下是我进行转换的方式:
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();
当两个片段都由 activity 的片段管理器管理时,这工作正常,但是当我像这样加载 ViewPager 时:
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
pager.setAdapter(new Adapter(getChildFragmentManager()));
ViewPager 的子项不受 activity 管理,它不再起作用。
这是 Android 团队的疏忽吗?有什么办法可以解决这个问题吗?谢谢
在activity
supportPostponeEnterTransition();
并且当您的片段被加载时(尝试同步它们,可能使用 EventBus 或其他)
startPostponedEnterTransition();
参考这个样本
可能您已经找到了这个问题的答案,但如果您还没有,下面是我经过几个小时的摸索之后修复它的方法。
我认为这个问题是两个因素的结合:
ViewPager
中的 Fragments
加载有延迟,这意味着 activity returns 比其包含在其中的片段快很多ViewPager
如果您像我一样,您的 ViewPager
的子片段很可能是同一类型。这意味着,它们都共享相同的过渡名称(如果您已在 xml 布局中设置它们),除非您在代码中设置它们并且仅在可见片段上设置一次.
为了解决这两个问题,我是这样做的:
1.修复延迟加载问题:
在您的 activity(包含 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。解决具有相同过渡名称的多个片段的问题:
现在有很多方法可以做到这一点,但这就是我在 "detail" activity 中最终得到的,即 activity 包含 ViewPager
(在 onCreate()
但你真的可以在任何地方做):
_viewPager.setAdapter(_sectionsPagerAdapter);
_viewPager.setCurrentItem(position);
...
...
_pagerAdapter.getItem(position).setTransitionName(getResources().getString(R.string.transition_contenet_topic));
您需要小心,因为 Activity
可能尚未附加到您的 ViewPager
片段,因此只需将过渡名称从 activity 传递到片段会更容易如果您从资源中加载它
实际实现和你想象的一样简单:
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
[=94= 之前重置 "visible" 片段上的过渡名称] 导航回来。这可以像这样轻松完成:
- 在您的
ViewPager
上收听页面更改
- 一旦您的片段不在视图中,请删除转换名称
- 在可见片段上设置转换名称。
- 不调用
finish()
,而是调用 ActivityCompat.finishAfterTransition(activity);
如果您向后过渡需要过渡到 RecyclerView
,这很快就会变得非常复杂,这就是我的情况。为此,@Alex Lockwood 在这里提供了一个更好的答案:ViewPager Fragments – Shared Element Transitions which has a very well written example code (albeit a lot more complicated than what I just wrote) here: https://github.com/alexjlockwood/activity-transitions/tree/master/app/src/main/java/com/alexjlockwood/activity/transitions
就我而言,我不必执行他的解决方案,我发布的上述解决方案适用于我的案例。
如果您有多个共享元素,我相信您可以想出如何扩展这些方法以满足它们的需求。
最近我一直在用这个把头撞在墙上。我想要的只是 ViewPager 中的一个 Fragment,以启动另一个具有良好的扩展卡类型共享元素转换的 Fragment。 None 上面的建议对我有用,所以我决定尝试启动一个 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>
然后从片段启动 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 布局中将 android:transition_name 放在 sharedView 上,在 Activity 布局中有 android:transition_name="target_transition"(与 Pair.create() 相同第二个参数)。
我的应用程序包含一个视图,该视图由一个包含少数片段的 ViewPager 组成。当您单击其中一个片段中的某个项目时,预期的行为是共享元素(在本例中为图像)过渡到显示有关所单击内容的更多信息的片段。
这是一个非常简单的视频,它应该是什么样子:
https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-114842.mp4
这只是使用 Fragment->Fragment 转换。
当您将起始片段放在 ViewPager 中时会出现问题。我怀疑这是因为 ViewPager 使用其父片段的子片段管理器,这与处理片段事务的 activity 的片段管理器不同。这是发生了什么的视频:
https://dl.dropboxusercontent.com/u/97787025/device-2015-06-03-120029.mp4
我很确定这里的问题正如我上面解释的那样是子片段管理器与 activity 的片段管理器。以下是我进行转换的方式:
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();
当两个片段都由 activity 的片段管理器管理时,这工作正常,但是当我像这样加载 ViewPager 时:
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
pager.setAdapter(new Adapter(getChildFragmentManager()));
ViewPager 的子项不受 activity 管理,它不再起作用。
这是 Android 团队的疏忽吗?有什么办法可以解决这个问题吗?谢谢
在activity
supportPostponeEnterTransition();
并且当您的片段被加载时(尝试同步它们,可能使用 EventBus 或其他)
startPostponedEnterTransition();
参考这个样本
可能您已经找到了这个问题的答案,但如果您还没有,下面是我经过几个小时的摸索之后修复它的方法。
我认为这个问题是两个因素的结合:
ViewPager
中的Fragments
加载有延迟,这意味着 activity returns 比其包含在其中的片段快很多ViewPager
如果您像我一样,您的
ViewPager
的子片段很可能是同一类型。这意味着,它们都共享相同的过渡名称(如果您已在 xml 布局中设置它们),除非您在代码中设置它们并且仅在可见片段上设置一次.
为了解决这两个问题,我是这样做的:
1.修复延迟加载问题:
在您的 activity(包含 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。解决具有相同过渡名称的多个片段的问题:
现在有很多方法可以做到这一点,但这就是我在 "detail" activity 中最终得到的,即 activity 包含 ViewPager
(在 onCreate()
但你真的可以在任何地方做):
_viewPager.setAdapter(_sectionsPagerAdapter);
_viewPager.setCurrentItem(position);
...
...
_pagerAdapter.getItem(position).setTransitionName(getResources().getString(R.string.transition_contenet_topic));
您需要小心,因为 Activity
可能尚未附加到您的 ViewPager
片段,因此只需将过渡名称从 activity 传递到片段会更容易如果您从资源中加载它
实际实现和你想象的一样简单:
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
[=94= 之前重置 "visible" 片段上的过渡名称] 导航回来。这可以像这样轻松完成:
- 在您的
ViewPager
上收听页面更改
- 一旦您的片段不在视图中,请删除转换名称
- 在可见片段上设置转换名称。
- 不调用
finish()
,而是调用ActivityCompat.finishAfterTransition(activity);
如果您向后过渡需要过渡到 RecyclerView
,这很快就会变得非常复杂,这就是我的情况。为此,@Alex Lockwood 在这里提供了一个更好的答案:ViewPager Fragments – Shared Element Transitions which has a very well written example code (albeit a lot more complicated than what I just wrote) here: https://github.com/alexjlockwood/activity-transitions/tree/master/app/src/main/java/com/alexjlockwood/activity/transitions
就我而言,我不必执行他的解决方案,我发布的上述解决方案适用于我的案例。
如果您有多个共享元素,我相信您可以想出如何扩展这些方法以满足它们的需求。
最近我一直在用这个把头撞在墙上。我想要的只是 ViewPager 中的一个 Fragment,以启动另一个具有良好的扩展卡类型共享元素转换的 Fragment。 None 上面的建议对我有用,所以我决定尝试启动一个 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>
然后从片段启动 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 布局中将 android:transition_name 放在 sharedView 上,在 Activity 布局中有 android:transition_name="target_transition"(与 Pair.create() 相同第二个参数)。