Transitions of a common element with fragments do not work with ViewPager

My application contains a view consisting of a ViewPager consisting of several fragments. When you click on an element in one of these fragments, the expected behavior for the common element (in this case, the image) is to go to the fragment that displays more information about the clicked content.

Here is a very simple video on how it should look:

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

This is just using a fragment -> fragment transition.

The problem occurs when placing the source fragment inside the ViewPager. I suspect this is because the ViewPager uses a child fragment manager of the parent fragment, which is different from the activity fragment manager that processes the fragment transaction. Here is a video of what is happening:

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

I am pretty sure that the problem here, as I explained above, is the child fragment manager and activity fragment manager. Here's how I make the transition:

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

This works fine when both fragments are managed by the activity fragment manager, but when I load the ViewPager as follows:

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

Children of ViewPager are not controlled by action, and it no longer works.

Is this oversight from the Android team? Is there any way to remove this? Thank.

+44
android android-viewpager android-transitions
Jun 03 '15 at 19:05
source share
3 answers

You probably already found the answer to this question, but if you didn’t do it, here’s what I did to fix it after a few hours of scratching my head.

The problem I'm thinking of is a combination of two factors:

  • Fragmnets loading of Fragmnets in ViewPager , which means that the activity returns much faster than its fragments contained within the ViewPager

  • If you look like me, then your child ViewPager are likely to be of the same type. This means that they all have the same transition name (if you specified them in your xml layout) , if you do not install them in the code and only install it once, on the visible fragment .

To fix both of these problems, this is what I did:

1. Fixing problems with delayed downloads:

Inside your activity (the one that contains the ViewPager ), add this line after super.onCreate() and before 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. Fixing a problem with several fragments with the same transition name:

Now there are quite a few ways to do this, but this is what I got inside my "detailed" action, that is, the activity that contains the ViewPager (in onCreate() , but you can do it anywhere):

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

You need to be careful, because Activity may not yet attach to your ViewPager fragment, so it’s easier to just pass the name of the transition from the operation to the fragment if you load it from a resource

The actual implementation is as simple as you expect:

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

Then inside the onViewCreated() add this line:

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

The final piece of the puzzle is to find out when your piece is fully loaded, and then call ActivityCompat.startPostponedEnterTransition(getActivity()); .

In my case, my fragments were not fully loaded until I load most of the things from the UI thread, which means that I needed to figure out a way to call this when everything was loaded, but if that is not your thing, you can call this right after calling setTransitionNameLollipop() .

The problem with this approach is that the transition to the output may not work if you are not very careful and reset the name of the transition to the "visible" fragment right in front of you exit activity to go back. This can be done as follows:

  • Listen to page changes on ViewPager
  • Remove the transition names (names) as soon as your fragment is not displayed.
  • Set the transition name to the visible fragment.
  • Instead of calling finish() call ActivityCompat.finishAfterTransition(activity);

This can become very difficult very soon if you need a transition to go to the RecyclerView , which was my business. For this, there is a much better answer provided by @Alex Lockwood here: ViewPager snippets - Transitions with a common element that has very well written sample code (although much more complicated than what I just wrote) here: https://github.com/ alexjlockwood / activity-transitions / tree / master / app / src / main / java / com / alexjlockwood / activity / transitions

In my case, I did not have to go so far as to implement his solution, and the above solution, which I wrote for my case.

If you have several common elements, I am sure you can figure out how to extend the methods to suit them.

+30
Jun 29 '15 at 19:13
source share

About the activity supportPostponeEnterTransition();

And when your fragments load (try synchronizing them, perhaps with EventBus or something else)

startPostponedEnterTransition();

Refer to this sample.

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

+1
Jun 21 '15 at 19:34
source share

I recently hit my head against a wall. All I wanted was a snippet in ViewPager to run another snippet with a good extension for the shared item with the extension of the open map. None of the above suggestions worked for me, so I decided to try running an Dialog-style 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> 

Then to start Activity from the Fragment:

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

In the fragment layout, install android: transition_name in the sharedView and in the Activity layout there is android: transition_name = "target_transition" (the same as the second argument to Pair.create ()).

0
07 Sep '16 at 9:39
source share



All Articles