How to fix fragments backstack issues in android - android

Hi everyone i have an issue in my code , i am using one main activity and two fragments (mainfragment,mainsubfragment) the application is about getting data from json parser and displaying it in mainfragment and when you click on it , it should display some data in mainsubfragment.The problem is that when i click on one of the display item in mainfragment its replace this fragment with mainsubfragment with key upon which it should get some more data from another json parser but instead it shows nothing and when i pressed back to mainfragment nothing display there as well.A quick help will be appreciated...

Create a singleton named NavigationHandler and add the below functions to it:
Function to open MainFragment:
public void openMainFragment(FragmentManager fragmentManager, MainFragment fragment){
String backStackName = fragment.getClass().getSimpleName();
fragmentManager.beginTransaction()
.replace(R.id.fl_main_container, fragment)
.addToBackStack(backStackName)
.commit();
}
Function to open SubFragment:
public void openSubFragment(FragmentManager fragmentManager, SubFragment fragment){
String backStackName = fragment.getClass().getSimpleName();
fragmentManager.popBackStackImmediate(backStackName, POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction()
.replace(R.id.fl_main_container, fragment)
.addToBackStack(backStackName)
.commit();
}
For backpress:
public void navigateBackBy(AppCompatActivity activity, int numOfFragments){
if(mFragmentManager.getBackStackEntryCount()==1){
activity.finish();
}
else {
int i;
for (i = 0; i < numOfFragments; i++) {
mFragmentManager.popBackStackImmediate();
}
}
}
In your Activity:
Call openMainFragment(...) to open the MainFragment with the list.
While calling your adapter constructor pass FragmentManager as a parameter.
Override onBackPressed() & add call navigateBackBy(this, 1);. This method is useful to go back by any no. of fragments.
Inside your adapter:
On click of an item call openSubFragment(...).

Welcome to stack over flow
According to you , you are using two fragment
1.mainfragment >> don't add to addToBackStack
2.mainsubfragment>> add to addToBackStack
for fragmetn1 call like as
Fragment fragment = new mainfragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();
for second fragment2
fragment = new mainsubfragment();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().addToBackStack(null).replace(R.id.content_frame, fragment).commit();
then it will be work and on back press also will be work

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.

Maintain fragments-backstack of previous Activity

I have 2 Activites A & B, In Activity A I managed fragment back stack but in one case as like below:
I have 4 fragments in Activity going from fragment 1 to 2 to 3 to 4 then from 4th fragment I'm going to Activity B now what i want is on press of back button fragment 4 of Activity should be opened and then on back fragment 3 and so on.
I'm using following function to replace the fragment and manage back stack
private void changeFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
fragmentManager.beginTransaction().replace(R.id.act_home_fl_container, fragment).commit();
}
On back press
boolean isPopFragment = getSupportFragmentManager().getBackStackEntryCount() > 0 ? true : false;
if (isPopFragment) {
getSupportFragmentManager().popBackStack();
}
I got success upto here but when I'm going to Activity B from 4th fragment and coming back to Activity A, I'm not able to manage back stack of fragments of Activity A.
I'm not having any how to start with it.
you can override onBackPressed inside your activities, check which fragment is on top and decide what to do as you wish:
#Override
public void onBackPressed()
{
// code here
super.onBackPressed(); // check if you need this, depends on your needs
}
Use this code to change fragment
public static void addFragment( final Fragment newFragment, final Fragment hideFragment) {
final FragmentManager fragmentManager = getFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(hideFragment);
fragmentTransaction.add(R.id.activity_home_container, newFragment, newFragment.getClass().getSimpleName());
fragmentTransaction.addToBackStack(hideFragment.getClass().getSimpleName());
fragmentTransaction.commitAllowingStateLoss();
}
newFragment "Fragment you want to add"
hideFragment "Fragment which is on container"
just use addToBackStack(tag_name) before commit
You can add en extension function in kotlin
fun AppCompatActivity.replaceFragment(containerId: Int, fragment: Fragment) =
supportFragmentManager.beginTransaction()
.replace(containerId, fragment, fragment.javaClass.name)
.addToBackStack(fragment.javaClass.name).commit()

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
}
}

Prevent the same Fragment to be added multiple times in popBackStack

My activity is composed of 3 nested Fragments. There is my MainFragment that is displayed by default, ProductFragment that can be called from it, then DetailFragment can be called from ProductFragment.
I can go back and forth between my ProductFragment and DetailFragment. By doing so, the popStackBack method is accumulating similar fragments. Then, if I click on the back button, It will go back through all the Fragments as many time I called them.
What is the proper way to avoid the same Fragment to be kept in the back stack ?
EDIT :
I firstly call my main fragment :
if (savedInstanceState == null) {
getFragmentManager()
.beginTransaction()
.add(R.id.container, new SearchFragment(), "SEARCH_TAG")
.commit();
}
Here is the code that calls the fragments from the activity :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_bottom, R.animator.exit_to_top, R.animator.enter_from_bottom, R.animator.exit_to_top);
ft.replace(R.id.container, new FactFragment(), "FACT_TAG");
ft.addToBackStack("FACT_TAG");
ft.commit();
Then, on back click :
#Override
public void onBackPressed() {
getFragmentManager().popBackStack();
}
I tried to get the tag of my current fragment and execute some specific code related to it but it doesn't work well. I also tried to addToBackStack() only when current Fragment wasn't already added to the backStack but it messed up my fragment view.
Use fragment's method isAdded() to evaluate the insertion. For example:
if(!frag.isAdded()){
//do fragment transaction and add frag
}
Here is my solution. Maybe dirty but it works. I implemented a method that returns the tag of the fragment that is displayed before clicking the on back button :
public String getActiveFragment() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getFragmentManager()
.getBackStackEntryAt(getFragmentManager()
.getBackStackEntryCount() - 1)
.getName();
return tag;
}
Then, on my onBackPressed() method :
// Get current Fragment tag
String currentFrag = getActiveFragment();
if(currentFrag.equals("PRODUCT_TAG")) {
// New transaction to first Fragment
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_from_right, R.animator.exit_to_left, R.animator.enter_from_right, R.animator.exit_to_left);
ft.replace(R.id.container, new SearchFragment(), "MAIN_TAG");
ft.commit();
} else {
// Go to Fragment-1
getFragmentManager().popBackStack();
}
Here is my handy and simple solution to check for duplicate insertion through fragment manager
at first, I check if it is first time intention for adding fragment and then I check if the fragment is presented using fragment manager
Fragment fragment = getSupportFragmentManager().findFragmentByTag("firstFragment");
if (fragment == null) {
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}else if(!fragment.isAdded()){
getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, new FirstFragment(), "firstFragment")
.addToBackStack(null)
.commit();
}
Here is my solution:
Fragment curFragment = fragmentManager.findFragmentById(R.id.frameLayout);
if(curFragment != null
&& curFragment.getClass().equals(fragment.getClass())) return;
// add the fragment to BackStack here
Xamarin.Android (C#) version:
var curFragment = fragmentManager.FindFragmentById(Resource.Id.frameLayout);
if (curFragment != null
&& curFragment.GetType().Name == fragment.GetType().Name) return;
// add the fragment to BackStack here

GetFragmentManager.findFragmentByTag() returns null

getFragmentManager().beginTransaction()
.replace(R.id.graph_fragment_holder, new GraphFragment(), "GRAPH_FRAGMENT")
.commit();
getFragmentManager().beginTransaction()
.replace(R.id.list_fragment_holder, new ListFragment(), "LIST_FRAGMENT")
.commit();
//getFragmentManager().executePendingTransactions();
GraphFragment graphFragment = (GraphFragment) getFragmentManager().findFragmentByTag("GRAPH_FRAGMENT");
graphFragment.setData(data);
ListFragment listFragment = (ListFragment) getFragmentManager().findFragmentByTag("LIST_FRAGMENT");
listFragment.setData(data);
I've supplied a tag so I'm not sure why findFragmentByTag() returns null.
What I've tried from reading other questions:
this.setRetainInstance(true) in the oncreate of both fragments.
Both fragment constructors are empty public fragmentName(){}.
tried executePendingTransactions after adding the fragments.
tried add instead of replace on the fragments (edited)
I was confused about this for a long time. First, you need to save the fragment you are replacing by pushing it onto the back stack. The tag you supply is put on the fragment you are adding, not the one you are pushing onto the back stack. Later, when you do push it onto the back stack, that tag goes with it. Here's code with objects broken out to make it easier to trace. You must call 'addToBackStack' before 'commit'.
GraphFragment grFrag = new GraphFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, grFrag, "GRAPH_FRAGMENT");
// grFrag is about to become the current fragment, with the tag "GRAPH_FRAGMENT"
tr.addToBackStack(null);
// 'addToBackStack' also takes a string, which can be null, but this is not the tag
tr.commit();
// any previous fragment has now been pushed to the back stack, with it's tag
ListFragment liFrag = new ListFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, liFrag, "LIST_FRAGMENT");
// liFrag is is about to become the current fragment, with the tag "LIST_FRAGMENT"
tr.addToBackStack(null);
tr.commit();
// 'grFrag' has now been pushed to the back stack, with it's tag being "GRAPH_FRAGMENT"
Call getFragmentManager().executePendingTransactions() after fragment transaction.
getFragmentManager()
.beginTransaction()
.replace(R.id.container, new ExampleFragment(), "YOUR TAG HERE");
.commit();
//after transaction you must call the executePendingTransaction
getFragmentManager().executePendingTransactions();
//now you can get fragment which is added with tag
ExampleFragment exampleFragment = getFragmentManager().findFragmentByTag("YOUR TAG HERE");
I was having the same problem of findFragmentByTag() always returning null.
Eventually I tracked it down, I was overriding onSaveInstanceState() in my Activity but not calling super. As soon as I fixed that findFragmentByTag() returned the Fragment as expected.
You can use
fragmentTransaction.addToBackStack(yourFragmentTag);
After that you can reuse it with
getSupportFragmentManager().findFragmentByTag(yourFragmentTag);
Answered here, just need to call getSupportFragmentManager().executePendingTransactions(); after your findByTag or findById
In my case I had to create a class level FragmentManager object and then use it instead of using getSupportFragmentManager() directly.
public class Main extends BaseActivity {
FragmentManager fragmentManager;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmain);
fragmentManager = getSupportFragmentManager();
initFrag1();
}
private void initFrag1() {
String name = Frag1.class.getSimpleName();
if (fragmentManager.findFragmentByTag(name) == null) {
fragmentManager.beginTransaction()
.add(R.id.frag_container, new Frag1(), name)
.addToBackStack(name)
.commit();
}
}
}

Categories

Resources