Material Design parent-child navigational transition recyclerview entry to detail fragment - android

Background
I am trying to implement the "parent-to-child" navigational transition specifically when you click a Recyclerview entry and the details appear in a fullscreen fragment. Something like this:
Question
How do I go about doing this with so many animation APIs available? (TransitionManager.beginDelayedTransition, SharedTransition, setExitTransition, etc)
What I have tried
InboxRecyclerView - This matches my requirements EXCEPT that it seems to only work when the detail view is in the same layout as the Recyclerview. Because I am navigating between fragments using the fragment backstack, I need it to transition between layouts that might not be available prior to attachment.
This post - Answers only cover activity-to-activity transitions. I am looking for fragment-to-fragment.
Custom Transition - I tried extending the Transition class, but I ran into the problems. CaptureStartValues() seems to capture values from the entire scene. I need it to only capture values from the recyclerview entry. Also, for some reason, the end values are not captured seeing as I get null endValues in the function createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues).
TransitionSet exitTransition = new TransitionSet()
.addTransition(new ChangeBounds())
.addTransition(new ChangeTransform())
.addTransition(new ChangeClipBounds())
.addTransition(new ChangeImageTransform())
.addTransition(new Expand())
.setOrdering(TransitionSet.ORDERING_TOGETHER);
TransitionSet enterTransition = new TransitionSet()
.addTransition(new Fade());
newFragment.setEnterTransition(enterTransition);
oldFragment.setExitTransition(exitTransition);

You should use shared element transitions here. There will be 2 shared elements: your RecyclerView's item background and TextView (for example). Your RecyclerView's item background should be stretched (you need to use ChangeTransform and ChangeBounds transitions) to your SecondFragment's background, and your TextView should be moved to SecondFragment's title. And you should use animations for your Shared Element transitions: firstly you should elevate item background of your RecyclerView, then you start stretching the background and moving your title, and in the end you should set your elevation back to normal. And note that all this time your first fragment should be visible, so you should set an exit duration to it: fragment1.setExitTransition(new Fade().setDuration(1).setStartDelay(<duration_of_transition>));.
So the main goal here is to use SharedElement transitions. There is a very good article aboud SharedElement fragment to fragment transitions.

Related

Shared element transition blocked by other views

I'm attempting to implement a shared element transition where I share some text from one fragment to another. The actual shared element animation worked fine until I attempted to combine it with a Fade transition. Now the shared element is being hidden behind the other fading content and is only visible once the fade finishes.
I'm animating from fragment A to fragment B.
In fragment A, I'm setting the exitTransition property in onCreate like so:
exitTransition = Fade()
In fragment B, I'm setting the enterTransition property in onCreate as well:
enterTransition = Fade()
The actual shared element transition is being triggered in the containing activity:
supportFragmentManager.commit {
replace(R.id.fragmentContainer, FragmentB.newInstance())
addSharedElement(sharedView, "transition_name")
setReorderingAllowed(true)
addToBackStack(null)
}
The animation used for the shared element is being set in onCreate of fragment B:
TransitionSet set = new TransitionSet();
set.setOrdering(TransitionSet.ORDERING_TOGETHER);
Transition changeBounds = new ChangeBounds();
changeBounds.addTarget(R.id.logo);
changeBounds.addTarget("logo_transition_name");
set.addTransition(changeBounds);
ChangeTransform changeTransform = new ChangeTransform();
changeTransform.addTarget(R.id.logo);
changeTransform.addTarget("logo_transition_name");
set.addTransition(changeTransform);
Transition textSize = new TextSizeTransition();
textSize.addTarget(R.id.logo);
textSize.addTarget("logo_transition_name");
set.addTransition(textSize);
sharedElementEnterTransition = set;
What am I missing?
Here's a gif for reference:
Ultimately I managed to fix this issue by setting a scale on the logo TextView that was transitioning.
To elaborate, my understanding from previous stack overflow answers written by George Mount is that the ChangeTransform transition will (kind of magically) handle reparenting a shared element view such that it won't get drawn over by another view.
After debugging the ChangeTransform transition, I found that its createAnimator method was never being called. It looks like that's because the view being shared didn't have any scale or rotation changes between the fragment A and fragment B.
Setting a scale of 0.99 on the logo TextView in fragment A lead to the createAnimator method being called and the view properly transitioning over other views instead of under them.
I suspect that since people are normally sharing imageviews that theres normally a scale difference between the two images so this problem doesn't show up as much. I could be wrong about that.

Fragment to Fragment Transition: postponeEnterTransition() does not work

I am using Android X transitions to animate content with motion between two Fragments, within the same hosting Activity.
I am trying to animate an ImageView which Image is loaded with Glide and an URL.
The problem I have is, regardless of the two ImageViews sizes, their size is 0 before the image is loaded, and because of that, the transitions capture wrong layout values and therefore show a shrinking animation (animating to size 0).
I have read about using postponeEnterTransition() which should, in association with startPostponedEnterTransition() delay the transition for the duration between the two calls.
This is the schema:
Fragment A, image is at the bottom of the screen, width is that of half of the screen's width.
Fragment B, image is at the top of the screen, width is that of the screen's width.
On Fragment A's image click, the Fragment B is loaded.
In Fragment B's onCreate I set my transitions using setSharedElementEnterTransition(transitions). Then I immediately call postponeEnterTransition();
In Fragment B's onCreateView, I await for the ImageView to be pre-drawn (using the ViewTreeObserver), after layout inflation, and then I call startPostponedEnterTransition().
Using a breakpoint and a listener on my transitions, the listener#onTransitionStarted is triggered before I reach the pre-draw callback, which proves the transition captured wrong layout values.
Also, another proof that postponeEnterTransition() has no effect whatsoever, removing the call to startPostponedEnterTransition() does not hold the transition forever.
What am I doing wrong ?
And of course, when returning to Fragment A, the same shrinking animation is being played out as well...
You should set reordering allowed during your fragment transaction by adding setReorderingAllowed(true) to your transaction.
See Android docs and the Reordering part of this blog post by Chris Banes for more context.

animate recyclerview to outside

I want to animate a recyclerview item to outside(sibling) of the recyclerview. I tried many different animation code and libraries but nothing happened. My problem is to animate a row of recyclerview to a linearlayout outside(sibling) of recyclerview.see attached picture.
My Main Layout flow is:
ConstraintLayout
--LinearLayout
----View (empty)
--Recyclerview
I believe you can use Shared element transition built-in from Android (search for more tutorial about this keyword)
It doesn't support the transition from within 1 view. But you can use Fragment to achieve the same result. Your activity will be like this:
ConstraintLayout (Activity)
--FrameLayout (container for the Fragment)
--Recyclerview
The Fragment contains the layout of your item's destination view.
I imagine the flow can be like this:
User taps on an item in RecyclerView in Activity
Replace new fragment to the container with defined data for shared element transition
https://medium.com/#bherbst/fragment-transitions-with-shared-elements-7c7d71d31cbb

Share element transition between recyclerview item inside viewpager fragment to viewpager fragment

I have two activities MainAcitity and DetailActivity. MainActivity is containing recyclerview inside the fragment of viewpager. When I click on item it will go to DetailActivity and show the detail info inside the fragment of viewpager in DetailAcitivty.
The Problems is:
I want to add transition when I click on recyclerview item in MainActivity and go to DetailActivity.
When I back from the DetailActivity to MainActivity I want its transition will come to the item that I swiped in viewpager.
How can I achieve this?
Note:
-MainAcitivty and DetailAcitivty both are containing viewpager.
Thanks!
You will need to tag both your views with a unique transitionName.
ViewCompat.setTransitionName(imageView, "some_unique_transitionName");
start the new activity with this new information
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, imageView, ViewCompat.getTransitionName(imageView));
activity.startActivity(intent, options.toBundle());
Now, on the new activity :
Depending on how you open your next activity and how complex is it's view hierarchy you'll might want to leverage supportPostponeEnterTransition(); during onCreate and supportStartPostponedEnterTransition();sometime later when it's set up. I'll let you read the documentation for it
Apart from that find the view on the new activity that needs to be animated and set the transition name on it
activityImage.setTransitionName(transitionName);
and let it rip.
few other things you might want to tinker with could be animation duration, interpolators etc.
getWindow().getSharedElementEnterTransition().setDuration(200);
getWindow().getSharedElementReturnTransition().setDuration(200)
.setInterpolator(new DecelerateInterpolator());
And, entry / exit transitions for your activities (you will need this)
Fade fade = new Fade();
fade.excludeTarget(android.R.id.statusBarBackground, true);
fade.excludeTarget(android.R.id.navigationBarBackground, true);
getWindow().setEnterTransition(fade);
getWindow().setExitTransition(fade);
Remember, the shared element transitions traverses the whole view hierarchy to find out the source and target views and only after the target activity is completely laid the framework reverse transitions to give you the illusion you want to achieve so yea, if the view hierarchies are complex or the activities take time to set up etc (Network dependent), you might need to tinker around a little bit more.

Shared elements animating between fragments

I'm trying to animate 2 simple Views from a selected item in a RecyclerView to a new fragment. I've looked at a lot of examples of animating shared elements from one Activity to another Activity, but very few examples of animating a shared element from one Fragment to another Fragment within the same Activity. It almost works.
Here is my structure.
Activity
-- Full screen Fragment1 with RecyclerView
-- Full screen Fragment2 with details
When the user selects an item in the RecyclerView in Fragment1, I replace Fragment1 with Fragment2 that has a View with the shared elements in it in different positions and sizes.
There's a bit of a trick to get it to work, you have to make sure your transitionName is unique for each item in your list, and of course that transitionName must match the transitionName of the element in Fragment2 for the animation to play. I have this part working, when I select an item, the 2 shared Views do animate, just not exactly how you would expect when doing it between 2 Activities.
If I select an item near the bottom of the screen, it draws the View for Fragment2 and animates the 2 shared Views as if they were in the item at the top of the screen. Hard to explain. Here are some pictures
Fragment1
Fragment2
In both fragments I'm setting the following
setSharedElementEnterTransition(new ChangeBounds());
setSharedElementReturnTransition(new ChangeBounds());
setAllowEnterTransitionOverlap(true);
setAllowReturnTransitionOverlap(true);
Also in their parent Activity in onCreate() I've set
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
Any idea why my shared element animations are starting at the top of my screen even when the they were starting in the selected item at the bottom of my screen?
Finally solved this problem! As it turns out because the view I'm sharing between 2 fragments is a child of another view (RelativeLayout) in the 2nd fragment, you need to add the ChangeTransform transition to your TransitionSet. Apparently ChangeTransform tells the system to remember the views original position in the 1st fragment before animating to the new position in the 2nd fragment. Here is my updated transitionSet. I'll also clean up my test project code a bit and make a final push to bitbucket in case it will help others after me. Thanks for all the help with this one Alex and thank you to #George-mount for answering someones similar question that dropped the hint to me for this solution.
<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
<changeTransform/>
<changeBounds/>
</transitionSet>
https://bitbucket.org/brockoli/fragmentsharedelements

Categories

Resources