Weird behavior in Fragment Backstack - android

I have an Activity that handles my fragments. I created the following method to add/replace fragments and add them (or not) to the backstack:
public void startFragment(CCFragment fragment, boolean addToBackStack) {
final String fragmentTag = fragment.getClass().getSimpleName();
final FragmentManager fragmentManager = getSupportFragmentManager();
// If my fragment is already in the backstack, I don't want to add
// it again, but go back to it:
boolean fragmentPopped=false;
if(fragmentManager.findFragmentByTag(fragmentTag)!=null){
fragmentPopped=true;
fragmentManager.popBackStack(fragmentTag,0);
}
//If it is not, I want to add/replace it
if (!fragmentPopped) {
fragment.setFragmentDelegate(this);
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
fragmentTransaction.replace(CONTENT_VIEW_ID, fragment, fragmentTag);
if (addToBackStack)
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
This works fine, until the following scenario happens:
startFragment(F1, false); //nothing in the backstack
startFragment(F2, true); //F1 in the backstack
startFragment(F3, false); //F1 in the backstack
startFragment(F1, false); -> when I call this, it enters the "if" and popBackStack won't work, so my app stays at the F3 instead of going back to F1. If I press the back button, then the app goes to F1...
So what am I doing wrong here? I already checked if the names are being stored right.

ft.addToBackStack(tag);
I'm not certain on this, but when you add a frag to the back stack, I believe you have to tag it (again). In your code, you're not supplying a tag, but using null, so there's no tag to search for, and even if it would otherwise use the original tag, you're overwriting it with null.
Edit: Use the following to verify the name of your tags on the backstack match the tags you originally assigned. I still believe your null is overwriting them.
FragmentManager mgr = getFragmentManager();
BackStackEntry be = mgr.getBackStackEntryAt(mgr.getBackStackEntryCount()-1);
String tag be.getName();
System.out.println("tag " + tag);

Related

fragment transaction - pop backstack and then add fragment

I am trying to do the following use case in Android Fragments. I have 2 fragments.
Fragment A -> Fragment B
When a user does something in Fragment B, I want to have the back stack as follows
Fragment A -> Fragment C. So, when the user presses back I want the user to go back to Fragment A.
I have tried the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitAllowingStateLoss();
The problem here is that I can see Fragment A for a short period of time before Fragment C is shown
If I do the following
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.addToBackStack(null)
.commitNowAllowingStateLoss();
I get the error
This transaction is already being added to the back stack
I can get Fragment C to show up if I do this BUT
mFragmentManager.popBackStackImmediate();
FragmentTransaction fragmentTransaction = fMgr.beginTransaction()
.replace(R.id.base, Fragment_C, "1")
.commitNowAllowingStateLoss();
This works and I don't see Fragment A and see Fragment C but the back button takes the user out of the application. So, is it possible that we can pop the back stack of the fragment and then add another fragment to the back stack w/o showing Fragment A AND the back button takes the user back to Fragment A
Here is an easy method to add fragments to fragments or to adapters within fragments...
from your base activity, make your fragment manager static. assume this activity is called dashboard.
static FragmentManager support;
Don't forget to initialize this in onCreate.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_dashboard);
support = getSupportFragmentManager();
define your new fragment inside your adapter or fragment.
users_item_fragment dialog = new users_item_fragment();
//also, let's add some data...
Bundle args = new Bundle();
args.putString("device", devicesList.get(position));
use the following method to add the fragment easily wherever you would like
//pick an easily remembered tag
public void replace(Fragment fragment, String tag){
FragmentManager man = dashboard.support;
FragmentTransaction fragt = man.beginTransaction();
if(!fragment.isAdded()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.add(R.id.fragment_container, fragment, tag)
.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
if(fragment.isAdded() && fragment.isHidden()) {
dashboard.lastTag = dashboard.fragtag;//not needed, but helpful w/ backpresses
fragt.show(fragment);
fragt.hide(man.findFragmentByTag(fragtag)).commit();
dashboard.fragtag = dashboard.tag;//not needed, but helpful w/ backpresses
}
}
To implement this with backpresses working correctly, add this in you onBackPress method of your main activity:
#Override
public void onBackPressed() {
FragmentManager man = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = man.beginTransaction();
fragmentTransaction.hide(getSupportFragmentManager().findFragmentByTag(fragtag))
.show(getSupportFragmentManager().findFragmentByTag(lastTag)).commit();
fragtag = lastTag;// holds the last fragment
}
}
It's easy to see the logic here and easy to manipulate back press events using this.

Back in fragment and nested fragment

i have app like this
one activity and inside it >
fragment a (loaded when run app also from menu can open it )
fragment b (open it from just menu)
fragment c (can open it from fragment a and also can open it from menu)
also inside fragment c there are 4 child fragments
in main activity(using navigation drawer as source) i call fragment a in oncreate like this
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_place,new First_Fragment()).addToBackStack("First").commit();
my problem is how to control back button to always back to fragment a and when fragment a is open close app
i was using addToBackStack(null) but is not what i want because will show all history of fragments that i opened
When adding fragment a to the back stack "addToBackStack(String name)" pass in a name.
Then listen for on back presses in your fragments
FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
}
});
make sure to stop listening when each fragment is not being shown.
Then you can pop back to the named fragment added to the back stack
FragmentManager fm = getActivity()
.getSupportFragmentManager();
fm.popBackStack ("name", FragmentManager.POP_BACK_STACK_INCLUSIVE);
Make sure the rest of your fragment transactions are not added to the back stack. This should give you the behavior you want.
addToBackStack(String tag) is used to add the fragment to backstack and it contains the string as parameter. This parameter can be null or have some value.
If you pass null, it will add your fragment to backstack with tag null. addToBackStack(null) doesn't mean that your fragment is not added to backstack.
If you want your fragment will not be added to backstack, then just delete this line.
If you are adding your fragment to backstack and want it to be visible onBackPressed, then you can use
getSupportFragmentManager().popBackStackImmediate(/* Fragment TAG */,0);
CODE:- Try the below code and let me know.
Copy the below function in your main Activity.
/**
* function to show the fragment
*
* #param name fragment to be shown
* #param tag fragment tag
*/
public void showFragment(Fragment name, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
// check if the fragment is in back stack
boolean fragmentPopped = fragmentManager.popBackStackImmediate(tag, 0);
if (fragmentPopped) {
// fragment is pop from backStack
} else {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, name, tag);
fragmentTransaction.addToBackStack(tag);
fragmentTransaction.commit();
}}
Show fragment using the below code.
showFragment(yourFragment, yourFragmentTag);
In mainActivity onBackPressed.
#override
public void onBackPressed(){
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() >= 2) {
// always show your fragment a here
showFragment(new FragmentA(), FragmentA.class.getSimpleName());
} else {
// finish your activity
}
}

Retain same instance of Fragment on configuration Change

I have the following issue. My application has 2 fragments, the first one that is executed show a list of items, when you tap on an item, shows a detail fragment.
I also have a NavigationDrawerActivity, with the following methods to handle the stack of fragments.
private void changeFragment(int pos, Fragment fragment, boolean addToBackStack) {
Fragment f = getSupportFragmentManager().findFragmentByTag(String.valueOf(pos));
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
mCacheServices.setmFragmentTag(String.valueOf(pos));
ft.replace(R.id.container, fragment, String.valueOf(pos));
if ((addToBackStack)) {
ft.addToBackStack(STACK_KEY);
}
ft.commit();
}
The problem i am facing is when i am in the DetailFragment, and i rotete the device, it goes to the ItemList fragment, which is the firs fragment excuted by the app. What can i do to mantain on the DetailFragment when i rotate the device?
Thanks!
you can save the state of the fragment and then re-create it. Go through this for more details
http://developer.android.com/guide/topics/resources/runtime-changes.html
hope this helps.

Retain Current Visible Fragment After Orientation Changes

In my application I have 3 Fragments. suppose fragment1, fragment2, and Fragment3. these three fragments hosted on single activity. there is a Navigation drawer available to navigate each fragment. and Fragment1 is the default fragment, ie., Fragment1 is visible when the application starts. the problem is when the orientation of phone changes the current visible fragment goes out and the default fragment shows, because the activity restarts. I am keeping a the tag of current visible fragment's tag in bundle, and checks in onCreate method. but I cant create Fragment object of corresponding fragment by tag.
onCreate
if (savedInstanceState == null) {
mFragment = new Fragment1();
showFragment(mFragment, FRAGMENT1_TAG);
} else {
String tag = savedInstanceState.getString(CURRENT_FRAGMENT_TAG);
mFragment = mFragmentManager.findFragmentByTag(tag);
if (mFragment != null)
mFragmentManager.beginTransaction().replace(R.id.content_frame, mFragment, tag).commit();
else
Toast.makeText(getBaseContext(), "Something went wrong, please restart application", Toast.LENGTH_SHORT).show();
}
onSaveInstanceState
String fragmentTag = mFragmentManager.findFragmentById(R.id.content_frame).getTag();
dataOut.putString(CURRENT_FRAGMENT_TAG, fragmentTag);
when the orientation changes I am getting a null pointer exception, that is the mFragment is null, how can I resolve this
UPDATE
public static void showFragment(Fragment fragment, String tag) {
mFragmentTag = tag;
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content_frame, fragment, mFragmentTag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
Keep the name of the class of your Fragments in the Bundle.
Then recreate your Fragment with the name of the class, using the static method instantiate.
String name = savedInstanceState.getString(CURRENT_FRAGMENT_TAG);
Fragment fragment = Fragment.instantiate (Activity.this, "com.example." + name);
The method expects the full name (package included).
The easiest way to do it is to prevent the activity from recreating by setting
android:configChanges="screenSize|orientation" in your manifest file.
UPDATE
You need to call to: super.onSaveInstanceState(outState); when overriding the onSaveInstanceState method

Removing a Fragment from the back stack

I have a 3 fragments in an activity when the a tablet is held in portrait. However I only have 2 of these fragments when in landscape. The problem I am having is when going from portrait to landscape the activity is creating the 3rd fragment. I receive and error as this fragment cannot be created.
I have worked out that this fragment is being created because it is in the back stack.
I have tried to remove the fragment in the onDestroy method by using
FragmentTransaction f = fragmentManager.beginTransaction();
f.remove(mf);
f.commit();
However the I get an error saying that I cannot use this function after the onSaveInstanceState
What would be the correct way of taking this fragment out of the back stack?
Update
I should probably add that the fragment I am having problems with is a mapFragment from this libary
https://github.com/petedoyle/android-support-v4-googlemaps
The way I use it is like so
mf = MapFragment.newInstance(1, true);
ft = fragmentManager.beginTransaction();
ft.replace(R.id.mapContainer, mf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack("map");
ft.commit();
You add to the back state from the FragmentTransaction and remove from the backstack using FragmentManager pop methods:
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(myFrag);
trans.commit();
manager.popBackStack();
I created a code to jump to the desired back stack index, it worked fine to my purpose.
ie. I have Fragment1, Fragment2 and Fragment3, I want to jump from Fragment3 to Fragment1
I created a method called onBackPressed in Fragment3 that jumps to Fragment1
Fragment3:
public void onBackPressed() {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.popBackStack(fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-2).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
In the activity, I need to know if my current fragment is the Fragment3, so I call the onBackPressed of my fragment instead calling super
FragmentActivity:
#Override
public void onBackPressed() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.my_fragment_container);
if (f instanceof Fragment3)
{
((Fragment3)f).onBackPressed();
} else {
super.onBackPressed();
}
}
you show fragment in a container (with id= fragmentcontainer) so you remove fragment with:
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
fragmentTransaction.remove(fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
In case you ended up here trying to figure out how to remove fragment(s) from backstack and you are using Android Jetpack Navigation component you could just use app:popUpTo and app:popUpToInclusive in action node of navigation graph xml to specify fragments that you want to pop out of back stack.
https://developer.android.com/guide/navigation/navigation-navigate#pop
Check this too
https://stackoverflow.com/a/51974492/4594986
What happens if the fragment that you want to remove is not on top of the stack?
Then you can use theses functions
popBackStack(int arg0, int arg1);
popBackStack(String arg0, int arg1);

Categories

Resources