81

I have an Activity and many fragments inflated in same FrameLayout

<FrameLayout
    android:id="@+id/content_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

example: mainActivity > any fragment (press back button) > activity is blank.

In onCreate:

layout = (FrameLayout)findViewById(R.id.content_frame);
layout.setVisibility(View.GONE);

When I start a fragment:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, profileFragment);
ft.addToBackStack(null);
ft.commit();
layout.setVisibility(View.VISIBLE);

I suppose I need to make the frameLayout's visibility GONE again on back pressed, but how do I do this?


I tried onBackPressed and set layout.setVisibility(View.GONE); but I cannot go back through fragments, as I go directly to main page.

MysticMagicϡ
  • 28,593
  • 16
  • 73
  • 124
Filip Luchianenco
  • 6,912
  • 9
  • 41
  • 63
  • If you came from the main activity, then you press the back button, what do you expect to get? – Ivan Skoric Dec 03 '13 at 00:08
  • i expect to get back to it, and actually see it. As the `FrameLayout` is still visible, i want to make it invisible, but i cannot ovverride onBackPressed, as i will not be able to go like: activity > fragment1 > fragment2 > onBackPressed it will go directly to activity and show it, and skip fragment2 – Filip Luchianenco Dec 03 '13 at 00:12
  • Just update your OnCreateView() override method on your fragment as stackoverflow.com/a/53813425/1298105 – Calyfs0 Dec 17 '18 at 10:53

13 Answers13

164

If you have more than one fragment been used in the activity or even if you have only one fragment then the first fragment should not have addToBackStack defined. Since this allows back navigation and prior to this fragment the empty activity layout will be displayed.

 // fragmentTransaction.addToBackStack() // dont include this for your first fragment.

But for the other fragment you need to have this defined otherwise the back will not navigate to earlier screen (fragment) instead the application might shutdown.

gabhor
  • 669
  • 9
  • 23
Sampath Kumar
  • 4,433
  • 2
  • 27
  • 42
47
@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    }
    else {
        int fragments = getSupportFragmentManager().getBackStackEntryCount();
        if (fragments == 1) {
            finish();
        } else if (getFragmentManager().getBackStackEntryCount() > 1) {
            getFragmentManager().popBackStack();
        } else {
            super.onBackPressed();
        }
    }
}

To add a fragment

 getSupportFragmentManager().beginTransaction()
                .replace(R.id.layout_main, dashboardFragment, getString(R.string.title_dashboard))
                .addToBackStack(getString(R.string.title_dashboard))
                .commit();
Goodlife
  • 3,822
  • 2
  • 24
  • 23
23

Sorry for the late response.

You don't have to add ft.addToBackStack(null); while adding first fragment.

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, profileFragment);
// ft.addToBackStack(null); --remove this line.
ft.commit();
// ... rest of code
irscomp
  • 2,900
  • 1
  • 16
  • 12
  • 2
    We must have to if we have this fragment opens another fragment. If we click back on the top one, it will close it. – MSaudi Nov 18 '14 at 13:46
  • Very useful, when we create an Activity with a fragment as landing UI. and then we can add all other fragments whenever required with addToBackStack(null);. – Hardian Apr 16 '19 at 16:49
22

If you want to track by the fragments you should override the onBackPressed method, like this

public void onBackPressed() { 
   if (getFragmentManager().getBackStackEntryCount() == 1) {
        finish();
   } else {
        super.onBackPressed();
   }
}
akhilesh0707
  • 6,709
  • 5
  • 44
  • 51
Edgar Sousa
  • 221
  • 2
  • 2
15

You can override onBackPressed and check to see if there is anything on the backstack.

@Override
public void onBackPressed() {
    int fragments = getFragmentManager().getBackStackEntryCount();
    if (fragments == 1) { 
        // make layout invisible since last fragment will be removed
    }
    super.onBackPressed();
}
MysticMagicϡ
  • 28,593
  • 16
  • 73
  • 124
JRomero
  • 4,878
  • 1
  • 27
  • 49
  • 1
    i checked the `fragments` value, it is always 0, even if it is second or third fragment. – Filip Luchianenco Dec 03 '13 at 00:32
  • See http://stackoverflow.com/questions/13964409/why-fragmentmanagers-getbackstackentrycount-return-zero – JRomero Dec 03 '13 at 00:36
  • thank you but i still get 0. I did `getFragmentManager().executePendingTransactions();` before every fragment, also i tried second method, `before fm.getBackStackEntryCount()` and still no luck. – Filip Luchianenco Dec 03 '13 at 00:45
  • 5
    Attention please, if you add/replace a fragment using supportedFragmentManager, you should also use getSupportFragmentManager().getBackStackEntryCount() and vise versa – David Apr 01 '14 at 18:00
4

Just don't add the first fragment to back stack

Here is the Kotlin code that worked for me.

    val ft = supportFragmentManager.beginTransaction().replace(container, frag)
    if (!supportFragmentManager.fragments.isEmpty()) ft.addToBackStack(null)
    ft.commit()
Sileria
  • 15,223
  • 4
  • 49
  • 28
2

On a recent personal project, I solved this by not calling addToBackStack if the stack is empty.

    // don't add the first fragment to the backstack
    // otherwise, pressing back on that fragment will result in a blank screen
    if (fragmentManager.getFragments() != null) {
        transaction.addToBackStack(tag);
    }

Here's my full implementation:

    String tag = String.valueOf(mCurrentSectionId);
    FragmentManager fragmentManager = mActivity.getSupportFragmentManager();
    Fragment fragment = fragmentManager.findFragmentByTag(tag);

    if (fragment != null) {
        // if the fragment exists then no need to create it, just pop back to it so
        // that repeatedly toggling between fragments doesn't create a giant stack
        fragmentManager.popBackStackImmediate(tag, 0);
    } else {
        // at this point, popping back to that fragment didn't happen
        // So create a new one and then show it
        fragment = createFragmentForSection(mCurrentSectionId);

        FragmentTransaction transaction = fragmentManager.beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                .replace(R.id.main_content, fragment, tag);

        // don't add the first fragment to the backstack
        // otherwise, pressing back on that fragment will result in a blank screen
        if (fragmentManager.getFragments() != null) {
            transaction.addToBackStack(tag);
        }

        transaction.commit();
    }
gMale
  • 17,147
  • 17
  • 91
  • 116
1

I still could not fix the issue through getBackStackEntryCount() and I solved my issue by making the main page a fragment too, so in the end I have an activity with a FrameLayout only; and all other fragments including the main page I inflate into that layout. This solved my issue.

M--
  • 25,431
  • 8
  • 61
  • 93
Filip Luchianenco
  • 6,912
  • 9
  • 41
  • 63
1

irscomp's solution works if you want to end activity when back button is pressed on first fragment. But if you want to track all fragments, and go back from one to another in back order, you add all fragments to stack with:

ft.addToBackStack(null);

and then, add this to the end of onCreate() to avoid blank screen in last back pressed; you can use getSupportFragmentManager() or getFragmentManager() depending on your API:

FragmentManager fm = getSupportFragmentManager();
    fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if(getSupportFragmentManager().getBackStackEntryCount() == 0) finish();             
        }
});

Final words: I don't suggest you to use this solution, because if you go from fragment1 to fragment 2 and vice versa 10 times, when you press back button 10 times it will do it in back order which users will not want it.

Jemshit
  • 9,501
  • 5
  • 69
  • 106
1

Almost same as Goodlife's answer, but in Xamarin.Android way:

Load fragment (I wrote helper method for that, but it's not necessary):

public void LoadFragment(Activity activity, Fragment fragment, string fragmentTitle = "")
{
    var fragmentManager = activity.FragmentManager;
    var fragmentTransaction = fragmentManager.BeginTransaction();

    fragmentTransaction.Replace(Resource.Id.mainContainer, fragment);
    fragmentTransaction.AddToBackStack(fragmentTitle);

    fragmentTransaction.Commit();
}

Back button (in MainActivity):

public override void OnBackPressed()
{
    if (isNavDrawerOpen()) drawerLayout.CloseDrawers();
    else
    {
        var backStackEntryCount = FragmentManager.BackStackEntryCount;

        if (backStackEntryCount == 1) Finish();
        else if (backStackEntryCount > 1) FragmentManager.PopBackStack();
        else base.OnBackPressed();
    }
}

And isNavDrawerOpen method:

bool isNavDrawerOpen()
{
    return drawerLayout != null && drawerLayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.Start);
}
Mehdi Dehghani
  • 10,970
  • 6
  • 59
  • 64
0

I had the same problem when dealing with Firebase's Ui Login screen. When back button was pressed it left a blank screen.

To solve the problem I just called finish() in my onStop() method for said Activity. Worked like a charm.

M--
  • 25,431
  • 8
  • 61
  • 93
0

If you have scenario like me where a list fragment opens another details fragment, and on back press you first need to show the list fragment and then get out the whole activity then, addToBackStack for all the fragment transactions. and then on the activity, do like this (courtesy: @JRomero's answer, @MSaudi's comment)

@Override
public void onBackPressed() {
    int fragments = getFragmentManager().getBackStackEntryCount();
    if (fragments == 1) { 
        // make layout invisible since last fragment will be removed
    }
    super.onBackPressed();
}
Shamsul Arefin
  • 1,771
  • 1
  • 21
  • 21
0

Just Comment or Remove transaction.addToBackStack(null) in your code.Below is code to change fragment in kotlin.

fun ChangeFragment(activity: MainActivity, fragment: Fragment) {

    val transaction: FragmentTransaction =
        activity.getSupportFragmentManager().beginTransaction()
        transaction.replace(R.id.tabLayoutContainer, fragment)
        transaction.commit()
}