The project is multiple-fragment project. Launch application to start BaseActivity, which all fragments are attached into it.
Issue:
Fragment A is the first fragment and is launched very well.
Fragment B is trigged by clicking button of Fragment A, also launched very well.
During from B back to A, the problem occurs. Nothing to display, only background, the BaseActivity.
All fragments use the api "replace" to display, although this API may produce performance problems, please ignore it firstly.
I have tried many methods, but nothing works.
The launched fragment method shows below:
public void navigateToFragment(Fragment fragment, boolean isClear){
if(isClear){
getSupportFragmentManager()
.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.replace(R.id.container,fragment)
.commit()
}
As I know, popBackStack and replace, commit can lead to one fragment is launched twice, but why no display after replace whatever I replace it?
try use add instead of replace
getSupportFragmentManager().beginTransaction()
.addToBackStack(null)
.add(R.id.container,fragment)
.commit()
Related
I have an Activity that creates a Fragment and then this Fragment creates another Fragment:
Activity -> Fragment1 -> Fragment2
I am now in Fragment2 and I'd like to go back to Fragment1 by clicking on a button.
In my OnClickListener of my button I have:
getActivity().getSupportFragmentManager().beginTransaction().remove(fragment2.this).commit();
This brings me to the Activity. Is there actually a way to just remove Fragment2 and go to Fragment1?
Thank you in advance for your time, I could not find any suitable info online!
#AhmedAbidi has a nice insight to your problem and yes, implementing popBackStack properly may solve your problem. But anyway, I would like to suggest a different approach to handle this type of situations.
Write two public functions in your Activity to switch between your fragments.
public void switchToFragment1() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new Fragment1()).commit();
}
public void switchToFragment2() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new Fragment2()).commit();
}
Now from the button click in your Fragment1 you might launch the Fragment2 via,
((YourActivity) getActivity()).switchToFragment2();
And the same thing while switching to Fragment1
((YourActivity) getActivity()).switchToFragment1();
Your question needs more code for clarity.
Is Fragment1 adding Fragment2 via getSupportFragmentManager() or getChildFragmentManager()? Presumably it appears you are doing the former, and if so, you are incorrectly using the Fragment API. Fragments are not supposed to know about each other, per Android's Fragment Documentation:
Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
You should therefore implement the appropriate listeners to communicate from Fragment1 to Activity, which can then decide where/when to add Fragment2 -- then properly utilize the back stack functionality of getSupportFragmentManager().addToBackStack()... with getSupportFragmentManager().popBackStack(). See Android Documentation on Back Navigation for Fragments for further explanation.
My app has 3 UI levels, each level has its own fragment, A -> B -> C.
I wish to optionally allow my app user to navigate straight to the top level fragment i.e. from C -> A without invoking B.
i.e., I still want to allow the user to go from C->B if they press the back button, but in the C fragment, I have a "Home" button, which takes them directly to A. This is the operation where I want to flush the backstack.
Additionally, I want the user to be able to go from B->A using the back button, hence I'm adding A and B both to the backstack.
I have tried the options from this SO post but in each case, onCreateView gets invoked for each fragment, even if I use fm_.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Also checked this thread, it seems to recommend using popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
Can anyone please suggest a way to pop the entire backstack without having the onCreateView called on popped fragments?
Dont add backStack line for going from fragment B->C
while going from A->B use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment) // LL_Fragment is container
.addToBackStack(null)
.commit();
while going B->C use
HighlightFragment highlightFragment=new HighlightFragment(FirstReaderScreen.this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LL_Fragment, highlightFragment)
.commit();
dont add line .addtobackstack(null)
This worked for me
i estimate you're using replace fragment.
try to use begintransaction.hide / show to manage many fragment inside 1 activity if you don want to popback
//on Activity
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(containerId, new FragmentA(), fragmentA.getClass().getSimpleName());
fragmentTransaction.commit();
//inside fragment A to jump fragment without PopBackStack
getFragmentManager().beginTransaction().hide(this).commit();
getFragmentManager().beginTransaction().show(new FragmentB()).commit();
This is working for me when needing to clear the back stack for tab switches in BottomNavigationView
public void clearBackStack() {
int backStackEntryCount = mFragmentActivity
.getSupportFragmentManager()
.getBackStackEntryCount();
for (int i = 0; i < backStackEntryCount; i++) {
mFragmentActivity.getSupportFragmentManager().popBackStack();
}
}
I'm implementing menu navigation using Fragments. So I begin with Home, and then users can navigate to diferent sections and details of each section.
When a user changes section, then I call pop on the fragmentmanager backstack until I reach Home, and then load the new section.
This is all working as expected. But I'm getting this problem:
load a section that calls setHasOptionsMenu(true) on onResume()
loads another section (old section it's suposed to get out of the stack). I see it OK. No menu is shown
leave the application (for example, go to Android Laucher activity) and then when I return, I see the correct section, but it's showing the Menu of the old Fragment.
I've iterated the backstack and printed each fragment, and there it's not the fragment with the menu.
I put a debug mark on the onResume() method (where the setHasOptionsMenu(true) is flagged) and it indeed enters here, so the Fragment it's still somewhere.
I want to know if I'm doing something wrong and how could I solve it, thx
Update:
I'm using this code to load new fragments
fm.beginTransaction()
.add(container, sectionFragment.getFragment())
.addToBackStack(sectionFragment.getFragmentName())
.commit();
And for remove:
private void clearStack(){
int count = fm.getBackStackEntryCount();
while(count > 1){
fm.popBackStack();
count--;
}
}
NOTE 1: I'm using add instead replace because I don't want to loose the state of my fragment when I navigate back from detail section. When I load another different section, then I call clearStack to pop the stack up to 1, and then loads new fragment. At the end, I'm calling executePendingTransactions() to finish to remove the fragments from the transaction.
NOTE 2: I'm seeing that it is entering on my fragment onDestroy() method, so it is suposed to be destroyed. But I don't know why it is getting called again when the Main activity resumes.
I found that the problem was not in the logic of adding and removing fragment of the stack.
The problem was that some of the fragment loaded another fragments inside of it (it had ViewPager component). Then I thought that when the fragment was removed then these fragments were removed too.
This is true ONLY if you use getChildFragmentManager() method. This method MUST be used when loading fragments inside other fragmets. If not, then the fragments are asociated with the fragments activity.
popBackStack will just revert your last FragmentTransaction.
If you use FragmentTransaction.add, popBackStack will just call FragmentTransacetion.remove.
But if you call FragmentTransaction.replace, popBackStack will call FragmentTransaction.remove and FragmentTransaction.add
For your "NOTE 1" :
FragmentTransaction.replace will not change your fragment state.
I found this question, because after calling
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
this code fragmentManager.getFragments().size() returns me the maximum number of fragments, which were in the stack. I checked every fragment on null. And I found that some fragment is null in my case. Maybe it will help someone)
If you are really looking to remove fragments at once then follow:
How to replace Fragments of different types?
Otherwise use replace transaction for fragments to smooth transitiona and hassel free approach, see https://stackoverflow.com/a/23013075/3176433
Also understand Fragment lifecycle,
http://developer.android.com/guide/components/fragments.html
I had a similar problem where the popBackStack() didn't remove my fragment.
However, I noticed that I called the wrong FragmentManager, where I had to call getSupportFragmentMananger() instead of getFragmentManager().
Maybe there is a <fragment> or <androidx.fragment.app.FragmentContainerView> in an activity with android:name="androidx.navigation.fragment.NavHostFragment", app:defaultNavHost="true" and app:navGraph="#navigation/nav_graph".
In this case navigation is held by nav_graph. If you don't want to use NavController and NavHostFragment, maybe you should remove navigation and clean <fragment> tag.
I need to detect backbutton pressed in Fragment.
My app start download data in Fragment, so I need to stop download (also do some operations) on back button pressed, and dont need if home button pressed. So I cant use onPause(), because its called in both cases.
I use addToBackStack(null), but I need to do some operations (stop download and ) in current fragment and then return to previous fragment.
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_frame, fragment)
.addToBackStack(null)
.commit();
When you are done with the things you want to do in the fragment you could use something like this to remove the current fragment. If last fragment was added to the stack (as you mentioned) then removing current fragment will automatically display last fragment.
this.getActivity().getSupportFragmentManager().beginTransaction()
.remove(this).commit();
or
this.getActivity().getFragmentManager().beginTransaction()
.remove(this).commit();
Look at this link
On yout activity you can overide onBackPressed() et then call popBackStack
When I start my app it runs an AsyncTask to load up and then in onPostExecute, I then setContentView to the new layout then add a fragment with two buttons offering two modes by an add FragmentTransaction. After one of the two modes is clicked, it then replaces the fragment with yet another FragmentTransaction using the replace method.
If the app crashes it returns to the first screen, loading up the two buttons offering the two modes. In this case if either mode is selected, the second fragment is loaded but is now the background is suddenly transparent showing the two buttons below and they remain clickable. If they are clicked again they properly replace the fragment so that it isn't visible below. This is just weird, I can't understand what could cause this.
I've researched and seen these two similar questions, one and two, which suggested that it might be because the ID is wrong or I have defined the fragment in XML. Neither of these two factors are the case.
My code is shown below:
Below I replace the loading screen.
#Override
protected void onPostExecute(Void result) {
setContentView(R.layout.activity_main_screen);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_container, new ModeFragment())
.commit();
}
After which, when a button is clicked I pass the fragment I wish to replace the current with into this method below:
private void replaceCurrentFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
}
This works the first time, however if a crash occurs then the app returns to the first fragment and the second time this method is passed, the new replacing fragment is semi-invisible. Clicking the button on the first fragment again calls this method again and it is now fine.
Obviously I don't want the app to crash so this shouldn't occur, but I get this feeling that there's something wrong with how I'm writing my code.
I've had the same problem happen to me, and it was because I loaded a fragment in the OnCreate of my Activity, without checking if there was a savedInstanceState, so android first reopen all old fragments, then do the OnCreate, which added the fragment over the old ones without replacing them so when you navigate to another fragment, it only replaces the top one, but not the bottom one, so you will see the fragments under it.
Might not be exactly the same thing for you, but it might help you figure it out.