94

I have a tabbed Actionbar/viewpager layout with three tabs say A, B, and C. In tab C tab(fragment),I am adding another fragment say fragment D. with

 DFragment f= new DFragment();
 ft.add(android.R.id.content, f, "");
 ft.remove(CFragment.this);
 ft.addToBackStack(null);
 ft.commit();

I modify actionbar in DFragment's onResume to add up button:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

Now in DFragment, when I press hardware(phone) Back button, I return to the original Tabbed(ABC) layout with CFragment selected. How can I achieve this functionality with actionbar up button?

QED
  • 9,803
  • 7
  • 50
  • 87
SohailAziz
  • 8,034
  • 6
  • 41
  • 43
  • Possible duplicate of [How to implement onBackPressed() in Fragments?](http://stackoverflow.com/questions/5448653/how-to-implement-onbackpressed-in-fragments) – Code-Apprentice Aug 22 '16 at 17:36

10 Answers10

189

Implement OnBackStackChangedListener and add this code to your Fragment Activity.

@Override
public void onCreate(Bundle savedInstanceState) {
    //Listen for changes in the back stack
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    //Handle when activity is recreated like on orientation Change
    shouldDisplayHomeUp();
}

@Override
public void onBackStackChanged() {
    shouldDisplayHomeUp();
}

public void shouldDisplayHomeUp(){
   //Enable Up button only  if there are entries in the back stack
   boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
   getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}

@Override
public boolean onSupportNavigateUp() {
    //This method is called when the up button is pressed. Just the pop back stack.
    getSupportFragmentManager().popBackStack();
    return true;
}
Roger Garzon Nieto
  • 6,554
  • 2
  • 28
  • 24
  • 22
    About `onSupportNavigateUp()`, "Method does not override method from its superclass". – Fred Feb 12 '14 at 14:30
  • This answer is more helpful than the accepted answer. Thanks a lot. – tinker Mar 09 '14 at 12:35
  • Thanks, this is easier and cleaner than the SohailAziz' answer! – javaxian May 01 '14 at 11:57
  • Should be accepted answer. I noticed that it does not work on the second level preference screen. How to handle that ? – Madhur Ahuja May 24 '14 at 17:08
  • 10
    If you already have a `onOptionsItemSelected`, it's also possible to check for the itemId `android.R.id.home` instead of adding `onSupportNavigateUp`. – domsom Sep 06 '14 at 12:22
  • Please note that it seems in some cases `onSupportNavigateUp()` isn't always called. For example, see [**this answer**](http://stackoverflow.com/a/22382533/708906). In my situation, running on an API 19 device using AppCompat Action Bar, `onNavigateUp()` is called instead. But this might change if it was run on an API < 11 device. So I think the safest solution would be to pop the back stack in `onOptionsItemSelected()` as some of the other answers suggest. – Tony Chan Nov 08 '14 at 01:14
  • 1
    If API version >= 14, Use onNavigateUp instead of onSupportNavigateUp @Override public boolean onNavigateUp() { //This method is called when the up button is pressed. Just the pop back stack. getFragmentManager().popBackStack(); return true; } – Tejasvi Hegde Nov 14 '14 at 06:49
  • 1
    Is there supposed to be an up caret that displays next to your app icon in the `ActionBar`? I don't see one when I implement this code. I am only able to click on the icon, but it doesn't do anything. Android 4.0+. – Azurespot Feb 04 '15 at 06:13
  • 3
    If onBackStackChanged() does not override, make sure your activity implements the FragmentManager.OnBackStackChangedListener interface. – CBA110 Mar 23 '16 at 19:58
  • What would be the difference with back navigation using this approach? – JavierSP1209 May 18 '16 at 15:11
43

I got it. just override onOptionsItemSelected in hosting activity and popup the backstack, e.g.

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home: {
            FragmentManager fm = getSupportFragmentManager();
            if (fm.getBackStackEntryCount() > 0) {
                fm.popBackStack();
                return true;
            }
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}

Call getActionBar().setDisplayHomeAsUpEnabled(boolean); and getActionBar().setHomeButtonEnabled(boolean); in onBackStackChanged() as explained in an answer below.

Anrimian
  • 4,257
  • 4
  • 22
  • 30
SohailAziz
  • 8,034
  • 6
  • 41
  • 43
18

If you have one parent activity and want this up button to work as a back button, you can use this code:

add this to the onCreate in your main activity class

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
            if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
                getSupportActionBar().setHomeButtonEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSupportActionBar().setHomeButtonEnabled(false);
            }
        }

    });

and then add onOptionsItemSelected like so:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
     ....
 }

I generally use this all the time and seems pretty legit

Daniel Jonker
  • 844
  • 10
  • 21
  • 1
    I tried using this code exactly, in an `Activity` of mine, in hopes that it would return to the fragment that it started from. The back button appears near my app icon when I go to my `Activity`, but I click the icon and nothing happens (it should go back to the fragment). Any idea why? Thanks. – Azurespot Feb 04 '15 at 05:42
  • 1
    @Daniel your code is legit.. it does work. You just might want to sorround it with try catch option just incase.. you know to prevent any unforeseen exceptions and apps crashing – Zuko Aug 31 '15 at 06:57
  • 1
    @NoniA. This is only going to go back to a previous fragment (e.g., fragment B -> fragment A), if you're inflating 1 fragment in a new activity, this won't go back to the prior activity. – Daniel Jonker Sep 03 '15 at 12:24
11

you can go back with up button like back button ;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            super.onBackPressed();
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Moaz Rashad
  • 1,035
  • 1
  • 10
  • 16
8

I used a combination of Roger Garzon Nieto's and sohailaziz's answers. My app has a single MainActivity, and fragments A, B, C that are loaded into it. My "home" fragment (A) implements OnBackStackChangedListener, and checks the size of the backStack; if it's less than one, then it hides the UP button. Fragments B and C always load the back button (in my design, B is launched from A, and C is launched from B). The MainActivity itself just pops the backstack on UP button tap, and has methods to show/hide the button, which the fragments call:

MainActivity:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }

fragmentA (implements FragmentManager.OnBackStackChangedListener):

public void onCreate(Bundle savedinstanceSate) {
    // listen to backstack changes
    getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);

    // other fragment init stuff
    ...
}

public void onBackStackChanged() {
    // enable Up button only  if there are entries on the backstack
    if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
        ((MainActivity)getActivity()).hideUpButton();
    }
}

fragmentB, fragmentC:

public void onCreate(Bundle savedinstanceSate) {
    // show the UP button
    ((MainActivity)getActivity()).showUpButton();

    // other fragment init stuff
    ...
}
Community
  • 1
  • 1
verboze
  • 419
  • 1
  • 7
  • 10
8

I know this question is old, but may be someone (like me) also needs it.

If your Activity extends AppCompatActivity, you can use a simpler (two-step) solution:

1 - Whenever you add a non-home fragment just show the up button, right after commiting the fragment transaction. Like this:

    // ... add a fragment
    // Commit the transaction
    transaction.commit();

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Then when UP button is pressed, you hide it.

@Override
public boolean onSupportNavigateUp() {
    getSupportActionBar().setDisplayHomeAsUpEnabled(false);        
    return true;
}

That's it.

martom
  • 327
  • 5
  • 10
5

This worked for me. Override onSupportNavigateUp and onBackPressed, for example (code in Kotlin);

override fun onBackPressed() {
    val count = supportFragmentManager.backStackEntryCount
    if (count == 0) {
        super.onBackPressed()
    } else {
        supportFragmentManager.popBackStack()
    }
}

override fun onSupportNavigateUp(): Boolean {
    super.onSupportNavigateUp()
    onBackPressed()
    return true
}

Now in the fragment, if you display the up arrow

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Clicking on it takes you back the previous activity.

bebe
  • 114
  • 2
  • 5
5

Kotlin:

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
        setupHomeAsUp()
    }

    private fun setupHomeAsUp() {
        val shouldShow = 0 < supportFragmentManager.backStackEntryCount
        supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
    }

    override fun onSupportNavigateUp(): Boolean = 
        supportFragmentManager.popBackStack().run { true }

    ...
}
fada21
  • 3,188
  • 1
  • 22
  • 21
2

This is a very good and reliable solution: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

The guy has made an abstract fragment that handles the backPress behaviour and is switching between the active fragments using the strategy pattern.

For some of you there maybe a little drawback in the abstract class...

Shortly, the solution from the link goes like this:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

    @Override
    public void onStart() {
        super.onStart();

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   

And usage in the activity:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}
Amio.io
  • 20,677
  • 15
  • 82
  • 117
  • While this link may answer the question, it is better to include the essential parts of the answer [here](http://meta.stackoverflow.com/a/8259) and provide the link for reference. Link-only answers can become invalid if the linked page changes. – bummi Jan 03 '15 at 11:56
  • BTW: if you identify duplicates, please flag them as such. thx. – bummi Jan 03 '15 at 12:02
  • is it important to setSelectedFragment inside onStart ? – VLeonovs Nov 15 '16 at 12:28
0

If you want to go back to your previous activity if this activity has an empty stack of fragments:

This could be useful if you have a MainActivity and you are navigating to e.g. a SettingsActivity with nested prefernceScreens. NavigateUp will pop fragments until you can finish the SettingsActivity to go back to parentActivity/root.

/**
 * On actionbar up-button popping fragments from stack until it is empty.
 * @return true if fragment popped or returned to parent activity successfully.
 */
@Override
public boolean onSupportNavigateUp() {
    //Pop back stack if the up button is pressed.
    boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
    if (canGoBack) {
        getSupportFragmentManager().popBackStack();
    } else {
        finish();
        return super.onSupportNavigateUp();
    }
    return true;
}

Note: setDisplayHomeAsUpEnabled(true); in fragment activities onCreate()

Benedict Lang
  • 103
  • 1
  • 4