Make replaced fragment visible while transition ongoing - android

Im using Navigation Architecture Component and i got my shared element transition between two fragments working fine. First fragment contains recyclerview and second one is a "details" of recyclerview item. The problem is that i need to keep first fragment visible while transition in progress.
How do i make transition like that ? Is there any option to achieve this with Navigation Architecture Component ? If no, what i need to use ?

The way exit transitions work is by adding the target views to a ViewOverlay that is added to the entering fragment. This means that, once the fragment is replaced, only the entering fragment exists and all the transitions applied to views that are exiting are actually happening on views that have been added to the entering fragment's ViewOverlay.
The reason why your exiting fragment doesn't stay visible is because you don't have an exit transition which means that no views are added to the ViewOverlay of the entering fragment.
Each view that is targeted in the exit transition is added to the ViewOverlay which sits on top of the entering fragments view hierarchy. So having the exiting fragment just sit there whilst the transition happens is not possible because once you target any views of the exiting fragment it will cause them to be on top of the view hierarchy, thus hiding all the other views (including the shared element).
Your best bet is probably to use a Fade transition as the exit transition and this way you can create a cross fade between the fragments

I dont know is it really possible with Navigation.
But its really possible with basic FragmentTransaction
fragmentManager!!
.beginTransaction()
.setReorderingAllowed(true) // setAllowOptimization before 26.1.0
.addSharedElement(view.imageIV, "fragment_news_iv")
.addSharedElement(view.title, "fragment_news_title_tv")
.addSharedElement(view.date, "fragment_news_date_tv")
.addSharedElement(view.container, "fragment_news_scrollView")
.addSharedElement(view.description, "fragment_news_description_tv")
.addToBackStack(null)
.setCustomAnimations(
android.R.anim.fade_in,
android.R.anim.fade_out,
android.R.anim.fade_in,
android.R.anim.fade_out
)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.add(
R.id.main_host_fragment,
NewsDetailsFragment(),
NewsDetailsFragment::class.java.simpleName
).addToBackStack(null)
.hide(this#MainFragment)
.commit()
Here is part of code which is doing transaction.
U can set custom animations like fade_in, fade_out with long duration(1000ms)
.setCustomAnimations(
android.R.anim.fade_in,
android.R.anim.fade_out,
android.R.anim.fade_in,
android.R.anim.fade_out
)
And set sharedEnterTransition in detailsFragment with duration 200
val trans = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
trans.apply {
duration = 200
enterTransition = trans
}
sharedElementEnterTransition = trans
So its obvious that first fragment will still be visible while transaction will be in progress. This is kinda workaround solution, but works)

Related

How to avoid reloading of child fragment inside parent fragment on BackPressed in Android

My main fragment has too many views to load because the lines of code in the file are increasing. To avoid this I decide to separate views using a child fragment. So now upper views are in the child fragment and the remaining bottom views are in the main fragment. Till this ok.
Now I am opening a new fragment by clicking one view from the main fragment. When I came back to the main fragment it is reloading the child fragment because of that I am getting NullPointerException and the app crashed.
Following is the way I am adding child fragments.
childFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commitAllowingStateLoss()
For more understanding.
I am using Navigation with BottomNavigationBar.
How to avoid this?
In some cases, fragment views are flickering when back to that fragment. How to avoid that?
For your first question you want to avoid putting the fragment in the backstack.
childFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)//<-- Here
.commitAllowingStateLoss()
For your second question Fragments are destroyed to conserve memory,typically happens when you can't see it. As such the recreation process could take some time, depending on how heavy the view is, thus the blinking.
The only way to stop it from blinking is to make sure the Fragment isn't doing so much that it can't seamlessly load back in.
You could do this by lazy loading your more heavy views, like lists, videos, and large images.
You could also look into Fragment transitions. These could visually smooth out the process of loading.
Here is a great source for some standard animations.
Fragment transaction animation: slide in and slide out

FragmentTransaction.TRANSIT_NONE is not working as expected

How can I switch to another fragment without any animation?
I have two fragments and show/hide them using this:
supportFragmentManager
.beginTransaction()
.hide(currentFragment)
.show(selectedFragment)
.setTransition(FragmentTransaction.TRANSIT_NONE)
.commit()
I expect them to switch without any animation, but there is a fade animation.
If I set the transition to TRANSIT_FRAGMENT_FADE, The fade animation will be shorter:
I'm using androidx version 1.6.0-alpha02 and kotlin version 1.4.32
Thank you.
FragmentTransactions sets the animation for a fragment entering or exiting the transaction.
show(): displays a previously hidden fragment. This is only relevant for fragments that have been added to the container. Show will use the FragmentTransaction that was used when the Fragment originally entered the container.
hide(): Hides the existing fragment. This is only relevant for fragments whose views have been added to a container, as this will cause the view to be hidden. Hide will use the FragmentTransaction that was set when the fragment originally entered the trasaction.
In your case when you call FragmentTransaction, it isn't setting the animation because you don't have a fragment entering or exiting the container, you are using hide and show to display your fragment so the FragmentTransaction is using the animation for when the fragments had originally entered the container.
If you wish to control the animation I would suggest using replace instead of show and hide`.
I found the problem.
It was because of Shared Element Transition that was setting in onCreate.
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
Removing this line fixed the problem.
However I wonder in case of having a shared element transition, how can I navigate to another fragment without fading animation...

Fragment enter and exit transitions are not executed at the same time

Running a simple slide to the left animation for both entering and existing fragment produces the effect of the entering fragment slightly overlapping with the exit fragment. This leads me to think that both transition are not executed at the same time. Any clue or confirmation of this behavior?
The desired effect is to slide the fragments to the left at the same time, without overlap.
The code:
Fragment current = ...;
Fragment fragment = ...;
Transition slideIn = TransitionInflater.from(this)
.inflateTransition(R.transition.fragment_indicator_enter)
.setDuration(300)
.setInterpolator(new LinearInterpolator());
fragment.setEnterTransition(slideIn);
currentFragment.setExitTransition(TransitionInflater.from(this)
.inflateTransition(R.transition.fragment_indicator_exit)
.setDuration(300)
.setInterpolator(new LinearInterpolator()));
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
The only workaround by know it has been to add a setStartDelay(30) for the entering transition. But weird thing, I have different transitions for different fragments and the startDelay has to be different to produce the effect of both fragment sliding to the left at the same time.
The effect is an expected behavior of the transition, as all the views in the layout are moved at a different time to avoid moving everything as a block a create some natural sense of motion. I intentionally want this block effect, so it's solved by adding a target for the transition, where this target is the FrameLayout containing the views of the fragment.
fragment.setEnterTransition(new Slide(Gravity.RIGHT)
.addTarget(R.id.whole_content));
Did you try placing the animations directly in the transaction call?
getSupportFragmentManager()
.setCustomAnimations(R.transition.fragment_indicator_enter, R.transition.fragment_indicator_exit)
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();

Android: how to move from one fragment to another without the user noticing?

I have two fragments - A and B - both fragments take up the entire screen. When I load my activity I show the user fragment A while in the background I load fragment B (in the fragmentmanager I do replace on fragment A and then on fragment B I do add and hide). Both fragments have relatively similar layouts (or at least it would be fair to say that the layout of A just before the transition is similar to the starting layout of B - views move around) and when I want to move from A to B I do a "show" on B and "hide" on A. It almost works perfectly, but you can still see a short flash of white in between, before B is shown, and it ruins the illusion that both are the same fragment. How can I transition without the user noticing?
A couple of words of explanation as to why there are two fragments and not one - the business logic is separate. Each fragment deals with a different part of the flow and it wouldn't make any sense to combine them.
Things I've tried so far:
• as I said above, I tried adding B in the background and only doing a "show" on it when it's needed - to save any setup time
• I tried overriding the pending transition and putting a fadeout/fadein, but it made no difference
• I tried hiding fragmentA instead of removing it when showing fragmentB, but it made no difference
• I tried overriding pending transition with 0,0 (getting rid of transitions entirely) but it's still the same
Code:
getSupportFragmentManager().beginTransaction()
.replace(R.id.contentBlock, fragmentA, "FRAGMENTA")
.add(R.id.contentBlock, fragmentB)
.hide(fragmentB)
.commit();
and later:
getSupportFragmentManager().beginTransaction()
.show(fragmentB)
.remove(fragmentA)
.commit();
mMapFragment = new MapFragment();
ft.beginTransaction(mMapFragment)
.detach(getactivity)
.attach(mMapFragment)
.commit();

Fragment ReenterTransition not working. Need help clarifying the various Fragment transitions

I am implementing Fragment transition animations between items in a RecyclerView, and a Fragment showing details of the clicked item. In other words the relatively common...
"Click on a Card in a list and it expands to a detailed view while the rest of the list disappears"
...kind of thing.
The transition from the RecyclerView item to the detailed view is working fine. The shared elements of the item are transitioning to their new state while the rest of the RecyclerView items fade away.
However, when the BackStack is popped the shared elements transition back to their old state, but the other RecyclerView items do not fade back in. They appear instantly at the start of the animation instead, as you can see in this Screen Video
The activity handles quite a few fragments, so I do the transaction in the following generalized method:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void setFragment(int fragId, Bundle args, List<Pair> transitionViews,
String tag, int containerId) {
// Setup the new fragment and transaction
Fragment newFragment = FragmentFactory.newFragment(fragId, args);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(containerId, newFragment, tag);
fragmentTransaction.addToBackStack(tag);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && transitionViews != null) {
// Add the shared elements
for (int i = 0; i < transitionViews.size(); i++) {
final Pair pair = transitionViews.get(i);
fragmentTransaction.addSharedElement((View) pair.first, (String) pair.second);
}
// Setup the transitions
Transition transitionMove = TransitionInflater.from(this).inflateTransition(android.R.transition.move);
Transition transitionFade = TransitionInflater.from(this).inflateTransition(android.R.transition.fade);
// transitionFade.setDuration(500); // Slow down the transition to help see what's happening
// Apply the relevant transitions to each fragment
newFragment.setSharedElementEnterTransition(transitionMove);
newFragment.setEnterTransition(transitionFade);
newFragment.setExitTransition(transitionFade);
mCurrentFragment.setExitTransition(transitionFade);
mCurrentFragment.setReenterTransition(transitionFade);
mCurrentFragment.setSharedElementReturnTransition(transitionMove);
}
fragmentTransaction.commit();
}
I have tried playing with allowing/disallowing transition overlap on Enter/Return.
I have tried playing with the various transition setting methods for Fragments.
I have read through loads of blogs and SO questions on this topic.
I found http://www.androiddesignpatterns.com/2014/12/activity-fragment-transitions-in-android-lollipop-part1.html blog on this topic, and brockoli's sample code very helpful, but have been unable to solve the problem.
Perhaps it is a problem with my understanding of what each transition is for?
Here's how I understand it.
My mCurrentFragment and newFragment have 5 different transition setters each:
setSharedElementEnterTransition Sets the Transition that will be used for shared elements transferred into the content Scene.
setSharedElementReturnTransition Sets the Transition that will be used for shared elements transferred back during a pop of the back stack.
setEnterTransition Sets the Transition that will be used to move Views into the initial scene.
setExitTransition Sets the Transition that will be used to move Views out of the scene when the fragment is removed, hidden, or detached when not popping the back stack.
setReenterTransition Sets the Transition that will be used to move Views in to the scene when returning due to popping a back stack.
When my setFragment method is called, an animation is played transitioning from mCurrentFragment to newFragment with the following properties:
The newFragment SharedElementEnterTransition defines how the shared elements will transition into newFragment.
(In my case the clicked item's CardView expands and one of the TextView's it contains is moved)
The newFragment EnterTransition defines how the remaining newFragment child views which are not shared elements will transition onto the screen.
(In my case a ToolBar fades in at the bottom of the screen. Actually the ToolBar is fading in behind the exiting RecyclerView. Is there any way to swap it so it's in front?)
The mCurrentFragment ExitTransition defines how the mCurrentFragment child views which are not shared elements will transition off of the screen.
(In my case mCurrentFragment only contains the RecyclerView, so the effect is that the rest of the RecyclerView elements fade away in the background)
When the BackStack is popped I would expect the following to occur:
The SharedElementReturnTransition for mCurrentFragment defines how the shared elements will transition back into mCurrentFragment.
(In my case the CardView contracts back down to RecyclerView item size and the TextView it contains is moved back).
The ExitTransition for newFragment defines how the newFragment child views which are not shared elements will transition off of the screen.
(In my case the bottom ToolBar fades out)
The ReenterTransition for mCurrentFragment defines how the remaining mCurrentFragment child views which are not shared elements will transition back onto the screen.
(In my case the other RecyclerView items should fade back in, but this is not happening. They are instantly visible behind the transitioning shared elements).
Have I misunderstood anything?

Categories

Resources