What I am trying to do is at onBackPressed I want create a new object of the top Fragment so I am trying this code:
if (getFragmentManager().getBackStackEntryCount() > 1) {
FragmentManager.BackStackEntry backEntry =getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount()-1);
String Back_fragment_name=backEntry.getName();
if(Back_fragment_name.equals("Frag1")){
Frag1 frag = new Frag();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag1").commit();
}else if(Back_fragment_name.equals("Frag2")){
Frag2 frag = new Frag2();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag2").commit();
}else if(Back_fragment_name.equals("Frag3")){
Frag3 frag = new Frag3();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag3").commit();
}
//getFragmentManager().popBackStack(); //Wrong
} else{
super.onBackPressed();
}
The problem is I think I should then remove the Top Fragment from BackStack but using popBackStack is wrong because it shows the the top fragment and i only need to remove it from BackStack.
any Ideas?
What I was trying to do is to implement the onBackPressed, that is pressing back button will transact the previous fragment, But I did not want to transact the previous Instance but a new one so I was using the Question's code. to do that I was adding fragments to BackStack in order to know which fragment is previous [the top one of the Backstack] then creating a new instance of it. So i was getting it's name then create it's new instance then delete it from the BackStack [Thats was the problem, I could not be able to delete it from backstack].
Finally I figured out that I do not need to use BackStack at All to know what are the previous fragments I just used an ArrayList of String to save every transacted Fragment's Name or tag so for example when transacting Frag1 I do this:
Frag1 frag1 = new Frag1 ();
fragmentTransaction.replace(R.id.RR, frag1 , "frag1").commit();
mPreviousFragmentsNames.add("frag1");
and onBackPressed will be like this:
if(!Removed_Last_Fragment_Name_From_backList){
// a boolean value, removing this will cause a bad behavior
// that is if back button pressed it transact current shown fragment first then u need to press back again to go to previous one
mPreviousFragmentsNames.remove(mPreviousFragmentsNames.size()-1);
Removed_Last_Fragment_Name_From_backList = true;
}
if (!mPreviousFragmentsNames.isEmpty()) {
String Back_fragment_name = mPreviousFragmentsNames.get(mPreviousFragmentsNames.size()-1);
if(Back_fragment_name.equals("Frag1")){
Frag1 frag = new Frag();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag1").commit();
}else if(Back_fragment_name.equals("Frag2")){
Frag2 frag = new Frag2();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag2").commit();
}else if(Back_fragment_name.equals("Frag3")){
Frag3 frag = new Frag3();
getFragmentManager().beginTransaction().replace(R.id.RR, frag , "Frag3").commit();
}
} else{
super.onBackPressed();
}
and It works Great, as Needed !
Related
In my main activity, i have a framelayout which is used to display fragments
now imagine, i have 3 fragment objects :
Fragment1 f1 = new Fragment1();
Fragment2 f2 = new Fragment2();
Fragment3 f3 = new Fragment3();
and i set this fragment using :
fragTrans.replace(android.R.id.content, f1);
fragTrans.addToBackStack(null);
fragTrans.commit();
now at different point of times, the app might have one of the three fragments in the framelayout (number of fragments might vary).
So, at some point if i want to identify which of the fragments if being currently displayed in the framelayout, how can i do that ?
My purpose :
the fragment2 has say 2 states, where boolean state can be either true or false, if the state is true, and the back button is pressed i want to do something and set state to false and the if the back is pressed i'll call super.onBackPressed() but if it is set to true and if currently fragment 3 is visible, first i want to go to fragment 2, then change the state to false and then super.onBackPressed()
so that will make it piece of cake if i can identify which fragment is currently visible
Try this
Fragment fragment = (Fragment)supportFragmentManager.findFragmentById(R.id.fragment_container)
Your Solution is here
1. First of you replace the fragment replace line with this line.
fragTrans.replace(android.R.id.content, f1,f1.getClass.getName());
Here f1.getClass,getName() is key of current Fragment. it Gives you name of fragment which is replace or current replace fragment.
FragmentManager manager = getSupportFragmentManager();
Fragment1 fragment1 = (Fragment1) manager.findFragmentByTag(Fragment1.class.getName());
Fragment2 fragment2 = (Fragment2) manager.findFragmentByTag(Fragment2.class.getName());
Fragment3 fragment3 = (Fragment3) manager.findFragmentByTag(Fragment3.class.getName());
if (fragment1 != null) {
Log.e(TAG, "Current fragment is Fragment1");
}else if(fragment2 != null) {
Log.e(TAG, "Current fragment is Fragment2");
}else if(fragment3 != null) {
Log.e(TAG, "Current fragment is Fragment3");
} else {
Log.e(TAG, "Fragment 1-2-3 is null");
}
If you do something on first time onBackPress Follow the code below,
#Override
public void onBackPressed() {
if (isBackPress) { //Default is false
super.onBackPressed();
} else {
// On First time click do something
isBackPress=true;
}
}
My app has this flow
Activity (TabLayout) -> Fragment 1 -> Fragment 2 -> Fragment 3
When I either press back on Fragment 2 or Fragment 3, I already go back to Fragment 1. I want to go back to Fragment 2 if I am on Fragment 3.
From fragment 1 to fragment 2
Frag2 childFrag = new Frag2();
getChildFragmentManager().beginTransaction().add(R.id.frag2_frame, childFrag)
.addToBackStack(null)
.commit();
From fragment 2 to fragment 3
Frag3 childFrag = new Frag3();
getChildFragmentManager().beginTransaction().add(R.id.frag3_frame, childFrag)
.addToBackStack(null)
.commit();
onBackPressed in Activity
// a fragments list for tablayout
ArrayList<Fragment> fragments = new ArrayList<>();
public void onBackPressed(){
fragments.get(selectedTab).getChildFragmentManager().popBackStack();
}
Try adding backStack
Frag2 childFrag = new Frag2();
getFragmentManager().beginTransaction().add(R.id.frag2_frame, childFrag)
.addToBackStack("Frag2 ")
.commit();
Once fragment 1 is added to the Activity, I believe you'll need to use getChildFragmentManager() to add additional fragments from a fragment. This method is available to classes that extend a Fragment.
Edit
As you've said you are using getChildFragmentManager(), I would use .beginTransaction().replace(...) instead of .beginTransaction().add(...).
Try to find the deepest fragment. Then popBackStack right there.
List<Fragment> fs2 = fragments.get(selectedTab).getChildFragmentManager().getFragments().get(0).getChildFragmentManager().getFragments();
FragmentManager f = fragments.get(selectedTab).getChildFragmentManager();
while (fs2.size() > 0) {
fs2 = fs2.get(0).getChildFragmentManager().getFragments();
f = f.getFragments().get(0).getChildFragmentManager();
}
f.popBackStack();
I have a problem about removing a specific fragment from back stack.My scenario is like this.Fragment-1 is replaced with Fragment-2 and then Fragment-2 is replaced with Fragment-3.
Calling order; Fragment-1-->Fragment-2-->Fragment-3.
When Fragment-3 is on the screen and then back button is clicked, i want to go
Fragment-1.That means i want to delete Fragment-2 from back stack.
How to do this ?
In the backstack you don't have Fragments, but FragmentTransactions. When you popBackStack() the transaction is applied again, but backward. This means that (assuming you addToBackStackTrace(null) every time) in your backstack you have
1->2
2->3
If you don't add the second transaction to the backstack the result is that your backstack is just
1->2
and so pressing the back button will cause the execution of 2->1, which leads to an error due to the fragment 2 not being there (you are on fragment 3).
The easiest solution is to pop the backstack before going from 2 to 3
//from fragment-2:
getFragmentManager().popBackStack();
getFragmentManager().beginTransaction()
.replace(R.id.container, fragment3)
.addToBackStack(null)
.commit();
What I'm doing here is these: from fragment 2 I go back to fragment 1 and then straight to fragment 3. This way the back button will bring me again from 3 to 1.
I had a very similar scenario to yours, my solution was just checking the amount of backStack transactions that I had.
If the transactions where more than 0 then I would simply pop it right away
so it would skip it when pressing back.
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStackImmediate();
}
...
fragmentTransaction.replace(R.id.main_fragment, newFrag, MAIN_FRAGMENT_TAG);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.commit();
This would successfully:
A -> B (back pressed) -> back to A
A -> B -> C (back pressed) -> back to A
The only downside that I see is that there is a quick flash where fragment A is displayed before going to fragment C.
If you are adding/launching all three fragments in the same activity, instead of the add(), use replace() (replace Fragment2 with Fragment3). The replace method removes the current fragment from backstack before adding the new one. If you are launching Fragment3 from a different activity, and thus you can't use replace(), remove Fragment2 from backstack before starting the new activity (which adds Fragment3):
// in Fragment2, before adding Fragment3:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.remove(this) // "this" refers to current instance of Fragment2
.commit();
fragmentManager.popBackStack();
// now go ahead and launch (add) fragment3
// if fragment3 is launched from a different activity,
// start that activity instead
fragmentManager.beginTransaction()
.add(R.id.a_container_view_in_activity, new Fragment3(),
Fargment3.FRAGMENT3_ID)
.commit();
Code for Fragment A -> Fragment B:
Add Fragment A in BackStack of Fragments
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_container, new fragmentB());
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Code for Fragment B -> Fragment C:
Do not Add Fragment B in BackStack of Fragments
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_layout_container, new fragmentC());
fragmentTransaction.commit();
It will works in this way: A -> B -> C and while returning C-> A as you excepted.
Hope it will help you.
for (int i = 0; i < mFragmentManager.getBackStackEntryCount(); i++) {
currentFragment = mFragmentManager.getFragments().get(i);
if (!(currentFragment instanceof ParticularFragment))
mFragmentManager.popBackStack();
else
break;
}
I have an activity with a container for fragments and a NavigationDrawer. If I select an item in the drawer, I call updatePage(index).
I also call updatePage(0) if I create the activity and the savedInstanceState == null to init my activity.
One fragment has a sub fragment and therefor it just replaces itself by the subfragment and adds the subfragment to the backstack, so that the user can navigate back to the previous fragment. In this fragment I call following code directly:
#Override
public void onClick(View view)
{
Event event = (Event)view.getTag();
FragmentManager fm = getActivity().getSupportFragmentManager();
GamesFragment f = new GamesFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(GamesFragment.KEY_EVENT, event);
f.setArguments(bundle);
FragmentTransaction ft = fm.beginTransaction()
.replace(R.id.frame_container, f, f.getClass().getName())
.addToBackStack(null);
ft.commit();
}
Why does sometimes the removing of the old fragment not work? I get overlaying fragments, but only sometimes.
My activities updatePage function looks like following:
private void updatePage(int drawerSelection)
{
mDrawer.closeDrawer();
Fragment f = null;
FragmentManager fm = getSupportFragmentManager();
switch (drawerSelection)
{
case 0:
f = fm.findFragmentByTag(HomeFragment.class.getName());
if (f == null)
f = new HomeFragment();
break;
case 1:
f = fm.findFragmentByTag(EventFragment.class.getName());
if (f == null)
f = new EventFragment();
break;
default:
break;
}
if (f != null && !f.isAdded())
{
// SOLUTION:
// Backstack clearen
// fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame_container, f, f.getClass().getName());
ft.commit();
}
}
I saw such situation few times and I bet that it can happen after replacing with addToBackStack. Easiest fix is to add background to fragment layout. From android doc:
Note: When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
This view is not redrawed entirely just overdrawed and if second fragment doesn't has background or view container redraw you will get effect as described. That's my theory :>. Sometimes Android documentation is not as clear as we would like it to be.
The problem is following use case:
Going to the EventFragment from the GameFragment pushes the "remove(EventFragment).add(GameFragment)" transaction to the backstack. If I now press the back button, this transaction will be undone and everything is fine. The backstack is empty again and everything works. BUT, if I don't press the back button, but change to another fragment through the menu, the backstack does still have the above mentioned transaction. Pressing back will now try to undo this transaction... It will just readd the EventFragment before the code in my menu click handler adds the fragment from the menu... This is how it could happen...
Easy solution, if I only want one backstack for every menu entry and want to delete the backstack, if I select another area of my app in the menu is, to clear the backstack before I go to another area through the menu...
So adding ' fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);' before replacing the the current fragment will solve the problem (I added this solution as a comment to my main post)
I have a MainActivity with a content_container to load Fragments in it.
In one of the Fragment I also have a sub_container to load Fragments.
In the MainActivity i manage the back button with a stack of Fragment:
public Fragment addFragmentToStack(Fragment fragment){
if(fragmentStack.size()>MAX_NUM_FRAGMENTS){
fragmentStack.remove(0);
}
return fragmentStack.push(fragment);
}
#Override
public void onBackPressed() {
if (fragmentStack.size() > 0) {
fragmentStack.pop();
if (MyApplication.fragmentStack.size() > 0) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_container, fragmentStack.peek());
ft.commit();
}
}
}
and whenever I want to replace a Fragment in my content_container I use
Fragment someFragment = new SomeFragment();
fragmentTransaction.replace(R.id.content_container, ((MainActivity)getActivity()).addFragmentToStack(someFragment), "");
fragmentTransaction.commit();
One Fragment is a gallery, with a menu to load a photos or a videos Fragment in it (so the gallery is the parent Fragment and the photos and videos Fragments are children).
In order to be able to add the photo gallery and the video gallery in the stack, I use in the GalleryFragment:
if(mType.equals("photos")){
Fragment videoFragment = new VideoFragment();
fragmentTransaction.replace(R.id.sub_container, videoFragment, "");
((MainActivity)getActivity()).addFragmentToStack(this);
}
else{
Fragment photoFragment = new PhotoFragment();
fragmentTransaction.replace(R.id.sub_container, photoFragment, "");
((MainActivity)getActivity()).addFragmentToStack(this);
}
i.e. I add the parent GalleryFragment in the stack.
When I go from the videos to the photos gallery, for instance, and press the back Button, the code in MainActivity's onBackPressed is executed, and the current GalleryFragment instance is replaced by the instance in the stack, but its sub_container is empty (none of onCreateView or onResume is called).
Anybody can help?
Ok, adding the same instance to the stack was not a good idea. Instead, if I add a new instance, it works perfectly, i.e. :
instead of
((MainActivity)getActivity()).addFragmentToStack(this);
use
GalleryFragment fragment = new GalleryFragment();
Bundle bundle = new Bundle();
bundle.putString("type", "photos"); // or bundle.putString("type", "videos");
// in order to know what to load once in there
fragment.setArguments(bundle);
((MainActivity)getActivity()).addFragmentToStack(fragment);