3

I'm trying to override the Back Button because it's closing my app when I push on, I have different Fragments:

  • Fragment A: Index (When I press back button, it's will close the app)
  • Fragment B (When I press back button it will go back to the Fragment A)
  • Fragment C (When I press back button it will go back to the Fragment A)

And I have my Main Activity: It manage my Fragments (for have a Navigation Drawer).

I found many posts on this but I can't implement them:

On Fragment B for exemple:

@Override
public void onBackPressed(){
    FragmentManager fm = getSupportFragmentManager();
    Fragment f = fm.findFragmentById(R.id.fragmentb); // get the fragment that is currently loaded in placeholder
    Object tag = f.getTag();
    // do handling with help of tag here
    // call super method
    super.onBackPressed();
}

It say cannot resolve onBackPressed() and getSupportFragmentManager(), I think I don't use the method correctely, so how to do ?

Activity:

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements FragmentDrawer.FragmentDrawerListener {

    private static String TAG = MainActivity.class.getSimpleName();
    private Toolbar mToolbar;
    private FragmentDrawer drawerFragment;

    //Initialisation de l activite avec les donnees necessaires
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        drawerFragment = (FragmentDrawer)getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
        drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), mToolbar);
        drawerFragment.setDrawerListener(this);
        // Affichage de la navigation
        displayView(0);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //Ajout des items
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onDrawerItemSelected(View view, int position) {
        displayView(position);
    }

    private void displayView(int position) {
        Fragment fragment = null;
        String title = getString(R.string.app_name);
        switch (position) {
            case 0:
                fragment = new Accueil();
                title = getString(R.string.title_accueil);
                break;
            case 1:
                fragment = new NosOffres();
                title = getString(R.string.title_nosoffres);
                break;
            case 2:
                fragment = new DemandeGratuite();
                title = getString(R.string.title_demandegratuite);
                break;
            case 3:
                fragment = new ContactezNous();
                title = getString(R.string.title_contact);
                break;
            case 4:
                fragment = new Actualites();
                title = getString(R.string.title_actu);
                break;
            case 5:
                fragment = new MentionsLegales();
                title = getString(R.string.title_mentions);
                break;
            default:
                break;
        }

        if (fragment != null) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.container_body, fragment);
            fragmentTransaction.commit();
            // libelle du toolbar
            TextView titlet;
            titlet = (TextView) findViewById(R.id.main_toolbar_title);
            titlet.setText(title);
            titlet.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/GothamBook.ttf"));
        }
    }
}

@jujyfruits

I tryedbut it doesn't work:

    @Override
    public void onBackPressed(){
        Fragment myFragment = getSupportFragmentManager().findFragmentById(R.id.demande_gratuite);
        if (myFragment != null && myFragment.isVisible()) {
            AlertDialog alertDialog = new AlertDialog.Builder(this).create();
            alertDialog.setTitle("Reset...");
            alertDialog.setMessage("test");
            alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            });
            alertDialog.show();
        }
        super.onBackPressed();
    }
}

@AutonomousApps

public class MainActivity extends AppCompatActivity implements FragmentDrawer.FragmentDrawerListener {

    private static String TAG = MainActivity.class.getSimpleName();
    private Toolbar mToolbar;
    private FragmentDrawer drawerFragment;

    //Initialisation de l activite avec les donnees necessaires
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
        getSupportActionBar().setDisplayShowHomeEnabled(true);
        drawerFragment = (FragmentDrawer)getSupportFragmentManager().findFragmentById(R.id.fragment_navigation_drawer);
        drawerFragment.setUp(R.id.fragment_navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout), mToolbar);
        drawerFragment.setDrawerListener(this);
        // Affichage de la navigation
        displayView(0);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //Ajout des items
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onDrawerItemSelected(View view, int position) {
        displayView(position);
    }

    private void displayView(int position) {
        Fragment fragment = null;
        String title = getString(R.string.app_name);
        switch (position) {
            case 0:
                fragment = new Accueil();
                title = getString(R.string.title_accueil);
                break;
            case 1:
                fragment = new NosOffres();
                title = getString(R.string.title_nosoffres);
                break;
            case 2:
                fragment = new DemandeGratuite();
                title = getString(R.string.title_demandegratuite);
                break;
            case 3:
                fragment = new ContactezNous();
                title = getString(R.string.title_contact);
                break;
            case 4:
                fragment = new Actualites();
                title = getString(R.string.title_actu);
                break;
            case 5:
                fragment = new MentionsLegales();
                title = getString(R.string.title_mentions);
                break;
            default:
                break;
        }

        if (fragment != null) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.container_body, fragment);
            fragmentTransaction.addToBackStack("name");
            fragmentTransaction.commit();
            // libelle du toolbar
            TextView titlet;
            titlet = (TextView) findViewById(R.id.main_toolbar_title);
            titlet.setText(title);
            titlet.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/GothamBook.ttf"));
        }
    }

    @Override
    public void onBackPressed(){
        FragmentManager mgr = getSupportFragmentManager();
        if (mgr.getBackStackEntryCount() == 0) {
            super.onBackPressed();
        }else{
             mgr.popBackStack();
        }
    }
BJM
  • 233
  • 5
  • 13
  • Your **activity** has the fragment manager (and I think also the `onBackPressed()` call), not the fragment. – EpicPandaForce Jul 23 '15 at 14:57
  • @EpicPandaForce But if I put Override public void onBackPressed() { super.onBackPressed(); } On MainActivity how I m gonna configure back button for the different Fragments ? – BJM Jul 23 '15 at 15:00
  • @benjy care to post the activity code? – milez Jul 23 '15 at 15:01
  • @benjy as the two answers say, you did not override onBackPressed in your activity. – milez Jul 24 '15 at 07:41
  • @milez I did here: Override public void onBackPressed(){ } – BJM Jul 24 '15 at 07:46
  • @benjy it is not in the activity code you posted? – milez Jul 24 '15 at 07:53
  • I think thats me I don't really understand sorry, Look my edited response to jujyfruits, thats not the good way to overrite ? – BJM Jul 24 '15 at 07:59
  • The code has to be placed to your activity class – milez Jul 24 '15 at 12:46
  • @milez that what i did, its my Main Activity – BJM Jul 24 '15 at 12:52
  • Your if-statement is wrong. `myFragment.isVisible()` will return `false` because the fragment you want to return to is NOT visible. You need to use `addToBackStack(String)` and then it's easier to check `getBackStackEntryCount()` and perform your operations there. See my answer for more details. – AutonomousApps Jul 24 '15 at 14:24
  • I believe that the reason you have to tap 'back' twice, even when your backstack entry count is 0, is because you are *always* calling `addToBackStack()` when you do fragment transactions. The very *first* transaction should *not* be added to the backstack. I explain in my revised answer. – AutonomousApps Jul 28 '15 at 14:04

3 Answers3

6

This is what I use when navigating between fragments:

MainActivity.java:

@Override
public void onBackPressed() {
    // note: you can also use 'getSupportFragmentManager()'
    FragmentManager mgr = getFragmentManager();
    if (mgr.getBackStackEntryCount() == 0) {
        // No backstack to pop, so calling super
        super.onBackPressed();
    } else {
        mgr.popBackStack();
    }
}

EDIT the second: Please note that you ONLY want to call super.onBackPressed() if you haven't already handled it (by, for example, popping the fragment manager's backstack).

For that to work, you have to add new fragments to your FragmentManager's backstack (addToBackStack()). For example (also in MainActivity.java):

private void displayView(int position) {
    Fragment fragment = ...; // YOUR CODE HERE
    if (fragment != null) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.container_body, fragment);

        // ADD THIS LINE
        fragmentTransaction.addToBackStack("name"); // name can be null

        fragmentTransaction.commit();
        // libelle du toolbar
        TextView titlet;
        titlet = (TextView) findViewById(R.id.main_toolbar_title);
        titlet.setText(title);
        titlet.setTypeface(Typeface.createFromAsset(getAssets(), "fonts/GothamBook.ttf"));
    }
}

EDIT the third (7/28): In your onCreate(Bundle) method, you do your very first fragment transaction by calling your displayView(int) method. displayView(int) always adds its fragment transactions to the backstack. This is not what you want. The very first fragment transaction should use fragmentTransaction.**add**(int, Fragment) and should not call addToBackStack(String). Every transaction after the first should call fragmentTransaction.**replace**(int, Fragment) and should call addToBackStack(String). The reason for this is that you first transaction is basically "adding" a fragment (your UI) to an empty container (it is not "replacing" another fragment). When this transaction is on the backstack, that means that the empty-container state is also on the backstack. So when you pop that last transaction, it displays a blank UI.

EDIT the first: When you call addToBackStack(String name) on a FragmentTransaction object (which you obtain by calling getFragmentManager().beginTransaction()), then you are adding a FragmentTransaction to your FragmentManagers 'backstack'. What my code does is check the size of the backstack by calling getFragmentManager.getBackStackEntryCount(). If that number is greater than zero, then we know we have FragmentTransactions on the backstack. In such a case, you can call getFragmentManager.popBackStack(), which will pop the last transaction off the backstack--in other words, returning your app to the last Fragment that was on display.

If the backstack entry county equals 0, then that means you're at your Fragment A, and you should instead call super.onBackPressed(), and this will cause the app to exit.

AutonomousApps
  • 4,229
  • 4
  • 32
  • 42
  • I don't understand the second code what I have to do in... and what is FragmentManager's backstack – BJM Jul 24 '15 at 07:36
  • see my additional explanation. Let me know if you need more information. You should definitely be reading the docs and the Google developer's guides for fragments. – AutonomousApps Jul 24 '15 at 13:26
  • I changed my example to reflect the code you are using for fragment transactions. – AutonomousApps Jul 24 '15 at 14:18
  • It's really important that you don't call `super.onBackPressed()` in your custom `onBackPressed()` method, UNLESS you're at your Fragment A and want to return to the home screen. That's why I wrapped my super call in an `else` statement. – AutonomousApps Jul 24 '15 at 14:20
  • Thanks ill try it, ill come back to you if I have a problem :) – BJM Jul 24 '15 at 14:36
  • I see I have to do some modifications to my actual code – BJM Jul 24 '15 at 14:48
  • It's work ! But I want to qui the app in the Fragment A and get back to the Fragment A in the other Fragments, how to do ? – BJM Jul 27 '15 at 09:31
  • And I have a problem, if i double tap on Back Button it put the Fragment in blank container – BJM Jul 27 '15 at 09:59
  • You say that backstack entry county equals 0 its to be on Fragment A, but I have to double tap to quit, if I tap on Back button on Fragment A i have a blank fragment – BJM Jul 27 '15 at 11:45
  • The problem is from `fragmentTransaction.addToBackStack("name");` when I add it and i push back the app doesn't close it show a blank fragment ive to tap back again to quit – BJM Jul 27 '15 at 12:05
  • Can you post your updated code? My first guess is that you're calling `.addToBackStack(String)` in your `onCreate(Bundle)` when you add your initial fragment--which you should not do. But I can't tell without seeing your code. – AutonomousApps Jul 27 '15 at 14:33
  • I'm calling addToBackStack in `if (fragment != null) {}`, look my edit, sorry to respond later. – BJM Jul 28 '15 at 07:03
  • You put `if (fragment != null) {}` in private void `displayView(int position) {} `but if i dot this on line `FragmentManager fragmentManager = getSupportFragmentManager();` i get an error "Unreachable statement" – BJM Jul 28 '15 at 07:06
  • The statement is unreachable because (I think) your switch statement includes `default: break;`, meaning it's possible that you never instantiate a fragment. My `displayView(int position)` method was just a copy of *your* method, with one-line modification of adding `addToBackStack(String)`. – AutonomousApps Jul 28 '15 at 14:01
  • 1
    Perfect explanations ! It look work correctely, but I have an another problem I can access to the another fragments from Accueil Fragment, do you know how to do do links like Navigation ? take a look here please: http://stackoverflow.com/questions/31674571/change-fragment-from-other-fragment-like-navigation-links But thank you for your time ! :) – BJM Jul 30 '15 at 07:44
  • Do you know how to do if I intent a new Activity after a Fragment, and go back to Main Activity on the back button and close if i do Back button again ? – BJM Aug 04 '15 at 13:32
  • You're going to have to ask a new question – AutonomousApps Aug 04 '15 at 14:26
  • If you want to see http://stackoverflow.com/questions/31826949/back-button-on-the-fragment-activity :) – BJM Aug 05 '15 at 08:14
0

onBackPressed() and getSupportFragmentManager() are methods from the Activity class, not Fragment, so you have to implement this on the Activity and not the Fragment. If you want to implement a particular behaviour just for one fragment you can check what's the current visible fragment and then implement your behaviour. Like this:

@Override
public void onBackPressed(){
    MyFragment myFragment =   (MyFragment)getFragmentManager().findFragmentByTag("YOUR_FRAGMENT");
    if (myFragment != null && myFragment.isVisible()) {
       // the code for whatever behaviour you want goes here
    }
    else
       super.onBackPressed();
}

Another possible, probably simpler way would be to use some flag in the Activity that you activate when you add the fragment and then read from the onBackPressed().

EDIT ON RESPONSE TO BENJY'S EDIT

Notice that you're not exactly using the code I posted. You're using findFragmentById() which doesn't apply to every situation and I can't tell if it's right for your code. You should use findFragmentByTag which applies to any kind of fragment transaction. Just add a tag to your fragment when you do the transaction, like this:

 getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment(), "FRAGMENT_TAG")
                    .commit();

Then when you try to get back the Fragment in your onBackPressed(), you get it looking for that tag:

PlaceholderFragment myFragment = (PlaceholderFragment) getSupportFragmentManager()
   .findFragmentByTag("FRAGMENT_TAG");

Make this change it has to work.

jujyfruits
  • 86
  • 6
  • @benjy that code was added in your activity class, right ? – jujyfruits Jul 24 '15 at 12:45
  • Yes, look up i did your code and add a message to see if it works but no way :) – BJM Jul 24 '15 at 12:51
  • @benjy look at my edit. It has to work if you change what I mention. I tried to make sure before posting and it worked for me. – jujyfruits Jul 24 '15 at 15:14
  • I agree with jujyfruits that you're better off using `findFragmentByTag()` – AutonomousApps Jul 24 '15 at 16:31
  • I have replace and you use add: `getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "FRAGMENT_TAG")commit();` and `getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "FRAGMENT_TAG")commit` are same ? – BJM Jul 27 '15 at 07:19
  • @benjy they are not the same, but that shouldn't be a problem for what you're trying to do. – jujyfruits Jul 27 '15 at 13:32
0

I know this too late for answer, but whoever, visits here, might got help with this, I achieved this through below.

if you want to change the fragment keeping the activity on click on device back button, above mentioned all codes will work with removing below line.

super.onBackPressed()