So, I have this code inside a setOnClickListener:
helpFragment = HelpFragment.newInstance()
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.addToBackStack(helpFragment.toString())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
But the problem is, every time I click the button a new instance of the fragment is instantiated. With that, for example, if I click 10 times in the button, I will have 9 fragments added to backstack and 1 visible. How can I create just one instance of the fragment? I have tried:
if (helpFragment == null)
But that obviously doesn't work...
Adding a Fragment to the back stack will retain fragments in the stack so that you can navigate back when needed.
You can still use the back stack but you have to check if the fragment is already added so that you don't have duplicated instances of the fragment in the stack.
E.g.
val helpFragment = HelpFragment.newInstance()
val isInBackstack = supportFragmentManager.findFragmentByTag(helpFragment.toString())
if (!isInBackstack) {
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.addToBackStack(helpFragment.toString())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
If you use addToBackStack, it'll always save the fragments to the backstack. Remove that line to not add the fragment to backstack. addToBackStack is used when there are multiple changes to the transaction, then all the changes are added in stack, and pressing back button will then restore those transactions one by one.
helpFragment = HelpFragment.newInstance()
supportFragmentManager
.beginTransaction() // Começar a transição
.replace(R.id.container, helpFragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() // Aplicar as alterações
}
It is better to see whole codebase of your problem. You can solve it with Kotlin's lazy. Check out this topic
Related
I have a menu where the user can click on different buttons and swap between different bottom bars.
When a user clicks button1 following happens:
setFragment(R.id.bottom_bar_container, new FooBottomBar());
When they click button2 it will call:
setFragment(R.id.bottom_bar_container, new BarBottomBar());
Heres the setFragment method:
private void setFragment(int layout, Fragment newFragment) {
String tag = newFragment.getClass().getCanonicalName();
Fragment fragment = getFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
fragment = newFragment;
}
Log.d("Tag", "Replacing with " + tag);
getFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.commit();
}
This works overall well but if user spam clicks on both buttons something interesting can happen.
Logcat:
Tag: Replacing with com.example.FooBottomBar
Tag: Replacing with com.example.BarBottomBar
But in the UI I can see FooBottomBar even though the last replacement was BarBottomBar.
I found that if I add addToBackStack(null) the problem goes away but I don't want to have this on the back stack.
getFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.addToBackStack(null)
.commit();
I would like to know the reason for this issue and if there's any solution to it.
Use getChildFragmentManager() with commitNow() instead.
getChildFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.commitNow();
I have an application with 4 fragments: MainFragment, ActionFragment, DoneFragment, FailedFragment. When application launched it shows MainFragment. Than application receive some event and show ActionFragment with two buttons 'yes' and 'no'. If user press 'yes', applicaiton shows DoneFragment, otherwise FailedFragment. When user press one time to back button on ActionFragment, DoneFragment or FailedFragment application must show MainFragment.
Improtant: if ActionFragment, DoneFragment or FailedFragment already opened and some event is occure again, application should show ActionFragment fragment with new event data.
So, I need:
if ActionFragment, DoneFragment or FailedFragment already opened and event occur, I should replace top fragment with ActionFragment
Otherwise I should simply add ActionFragment.
I am trying:
fun addOrReplaceFragment(fragment: Fragment, tag: String) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
val previous = fragmentManager.findFragmentByTag(tag)
if (previous == null) {
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
fragmentTransaction.add(R.id.main_fragment_container, fragment, tag)
fragmentTransaction.addToBackStack(tag)
} else {
fragmentManager.popBackStack(previous.id, 0)
fragmentTransaction.remove(previous)
fragmentTransaction.add(R.id.main_fragment_container, fragment, tag)
fragmentTransaction.addToBackStack(tag)
}
fragmentTransaction.commitAllowingStateLoss()
}
// ...
addOrReplaceFragment(ActionFragment(), "singleTag")
// ...
addOrReplaceFragment(DoneFragment(), "singleTag")
// ...
addOrReplaceFragment(FailedFragment(), "singleTag")
Here is popBackStack() doesn't work. When ActionFragment is opened, DoneFragment or FailedFragment just adding above. And user have to press back two times to get back to MainFragment.
I am find solution change popBackStack() to popBackStackImmediate(). It works well, but if activity is minimized it produce crash with IllegalStateException, because popBackStackImmediate() cannot be called after onSaveInstanceState().
How to replace top fragment and avoid IllegalStateException?
Try this :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.FragmentToBeReplaced,theFragmentToBeAdded);
ft.commit();
https://developer.android.com/reference/android/app/FragmentManager.html#isStateSaved()
Use isStateSaved to avoid losing state when a transaction happened.
And I think Android navigation component might be easier way to navigate between fragments.
Why does my fragment manager's getBackStackEntryCount method return zero?
I've spent an hour or checking SO for an answer to this question. I've
used the fragment support manager with getSupportFragmentManager
called executePendingTransactions
imported android.support.v4.app.Fragment
but getBackStackEntryCount keeps returning zero.
Any idea why? Doesn't my code look correct?
Here's my code
private void injectFragment(){
// Get the fragment
StepsFragment stepsFragment = new StepsFragment();
// Get the support fragment manager
FragmentManager fragmentManager = getSupportFragmentManager();
// Inject the fragment into a frame layout
fragmentManager.beginTransaction()
.replace(R.id.frame_layout, stepsFragment)
.commit();
// I read that I should execute pending transactions before calling
// getBackStackEntryCount
fragmentManager.executePendingTransactions();
// This log prints `getBackStackEntryCount returns 0`
int entryCount = fragmentManager.getBackStackEntryCount();
Log.v(TAG,"getBackStackEntryCount returns "+String.valueOf(entryCount));
}
getBackStackEntryCount() is 0 because you have not added any Fragment transactions to the FragmentManager's back stack.
Note that each FragmentManager has its own backstack (so a Fragment's child FragmentManager stack would be different from the containing Activity's fragment backstack), and it is also distinct from the application's Activity back stack.
If you were to add your transaction to the back stack like so, it would return 1:
fragmentManager.beginTransaction()
.replace(R.id.frame_layout, stepsFragment)
.addToBackStack(null)
.commit();
In my Activity, I add the first Fragment (myFragmentA) in onCreate() method:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
.add(containerViewId, myFragmentA, fragmentTag)
.addToBackStack(null)
.commit();
In this first fragment, when I click on a Button, I add a new fragment (myFragmentB):
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
.replace(containerViewId, myFragmentB, fragmentTag)
.addToBackStack(null)
.commit();
All is great to this point.
When I catch back pressed from Activity:
#Override
public void onBackPressed() {
// >>> Just to prevent to keep the first fragment !!
if (getSupportFragmentManager().getFragments().size() > 1) {
getSupportFragmentManager().popBackStack();
}
}
Back pressed from myFragmentB > myFragmentA : OK
Back pressed from myFragmentA (just to verify there is no popback) : FAILED. myFragmentA is removed too! And I want to keep it always. I don't know why getSupportFragmentManager().getFragments().size() is equal 2!
Thanks for your helps guys!
If i correctly understood.
You want to use
getBackStackEntryCount()
instead of
getFragments().size()
Take a look at the docs: http://developer.android.com/reference/android/support/v4/app/FragmentManager.html
You see, your container activity gets added to the back stack by default. In your code you have written addToBackStack(null) for both the fragments, this will add your fragments to the back stack over and above your activity which gets added to the back stack by default. This explains why you are seeing size 2, one may be your activity and the other your fragment.
I am trying to make a shared element transition between fragments, everything works fine when using replace() to add the second fragment, however in the codebase add() is used a lot, but when using that, transition just skips to end values
Is it possible to have the transition between added fragments?
Thanks
#Override
public void onClick(View v) {
setSharedElementReturnTransition(TransitionInflater.from(getActivity())
.inflateTransition(android.R.transition.move));
FragmentB secondFragment = new FragmentB();
secondFragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity())
.inflateTransition(android.R.transition.move));
getFragmentManager().beginTransaction()
.add(R.id.container, secondFragment)
.addToBackStack(null)
.addSharedElement(imageView, imageView.getTransitionName())
.commit();
}
Try this
getSupportFragmentManager().beginTransaction()
.addSharedElement(myImage, "mytransition")
.add(R.id.recycler_view_container, myFragment2)
.hide(myFragment1)
commit();
worked for me
since the system isnt going through the onPause from the first fragment its not going to happen. becuase when you add a new fragment, the new fragment comes on the top of the old fragment.
but you can fake it though you will have more code !
there is a sample below:
https://github.com/Kisty/FragmentTransitionExample
and a video not compeletely related but helps you to get the idea:
https://www.youtube.com/watch?v=CPxkoe2MraA
Try add .detach() method for FragmentTransaction.
FragmentManager manager = activity.getSupportFragmentManager ();
Fragment currentFragment = manager.findFragmentById (CONTAINER_ID);
int intoContainerId = currentFragment.getId ();
manager.beginTransaction ()
.setTransition (FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.addSharedElement(view, transitionName)
.addToBackStack (withTag)
.detach(currentFragment)
.add(intoContainerId, newFragment, withTag)
.commit();