Comment afficher une boîte de dialogue pour confirmer que l'utilisateur souhaite quitter une activité Android?


274

J'ai essayé d'afficher un "Voulez-vous quitter?" type de boîte de dialogue lorsque l'utilisateur tente de quitter une activité.

Cependant, je ne trouve pas les crochets API appropriés. Activity.onUserLeaveHint()Au début, cela semblait prometteur, mais je ne trouve pas de moyen d'empêcher l'activité de se terminer.


39
Est-il absolument essentiel que vous ayez cette invite? Lorsqu'un utilisateur souhaite terminer une activité, il doit pouvoir le faire immédiatement. Vous voudrez peut-être repenser votre stratégie.
Tom R

4
L'affichage d'une option de confirmation de sortie est obligatoire pour l'inscription sur l'App Store de Samsung. Une de mes applications a été refusée pour ne pas l'avoir.
John Ashmore

6
C'est désagréable, @Samsung.
dokkaebi

3
Cela dépend de ce que vous voulez faire en sortant. Si l'état est conservé et que vous pouvez simplement revenir en arrière, l'affichage d'une boîte de dialogue peut ne pas être nécessaire. Cependant, dans l'une de mes applications, je nettoie définitivement les cookies, les données et les connexions étroites avec une validation finale des données. L'utilisateur doit être conscient de la finalité de son choix, d'autant plus qu'il est rare aujourd'hui d'avoir ce sens de la finalité dans une application mobile.
Eric

15
@ Tom R: Totalement en désaccord. Il existe des applications que vous ne souhaitez pas terminer par accident. Ils travaillent dans un environnement commercial et ainsi de suite.
TomeeNS

Réponses:


390

Dans Android 2.0+, cela ressemblerait à:

@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle("Closing Activity")
        .setMessage("Are you sure you want to close this activity?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();    
        }

    })
    .setNegativeButton("No", null)
    .show();
}

Dans les versions antérieures, cela ressemblerait à:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    //Handle the back button
    if(keyCode == KeyEvent.KEYCODE_BACK) {
        //Ask the user if they want to quit
        new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.quit)
        .setMessage(R.string.really_quit)
        .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

                //Stop the activity
                YourClass.this.finish();    
            }

        })
        .setNegativeButton(R.string.no, null)
        .show();

        return true;
    }
    else {
        return super.onKeyDown(keyCode, event);
    }

}

15
Toujours dans 2.0 et au-dessus, il y a un nouvel événement onBackPressed qui est recommandé par rapport à onKeyDown developer.android.com/intl/zh-TW/reference/android/app/… Il y a une section ici qui parle des changements et de la nouvelle approche recommandée. developer.android.com/intl/zh-TW/sdk/android-2.0.html
Patrick Kafka

3
Article de blog sur la capture de la touche de retour ici: android-developers.blogspot.com/2009/12/… Notez que cela ne vous permet pas d'attraper d'autres façons dont l'utilisateur peut quitter votre application: appuyer sur la maison, sélectionner une notification, recevoir un téléphone appel, etc.
hackbod

Cela fonctionne parfaitement, mais comment forcer l'arrêt du code pendant qu'il est montré à l'utilisateur?
anon58192932

trouvé, c'est une excellente solution ici: stackoverflow.com/questions/4381296/…
anon58192932

1
C'est la méthode () que je recherche mais je ne savais pas où appeler cette méthode ().
user2841300

186
@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   ExampleActivity.super.onBackPressed();
               }
           })
           .setNegativeButton("No", null)
           .show();
}

5
ElBradford a fait un commentaire: appeler le super onBackPressed est mieux que de supposer que onBackPressed n'appellera que finish (). Même si c'est vrai maintenant, ce ne sera peut-être pas le cas dans les futures APICustomTabActivity.super.onBackPressed
mplungjan

@mplungjan Pouvez-vous clarifier votre commentaire ... Êtes-vous en train de dire qu'il vaut mieux remplacer le finish()code par super.onBackPressed()?
ban-

Le commentaire a 3 ans. Je n'ai aucune idée de ce qu'est une bonne pratique en 2015
mplungjan

@mplungjan Votre commentaire faisait référence aux futures API, mais il serait utile de clarifier quand même.
ban-geoengineering

Pas de problème. Eh bien, je viens d'essayer MyActivity.super.onBackPressed();et cela fonctionne bien pour moi. Il semble que l'approche la plus logique soit aussi - d'appeler la méthode que vous remplacez si vous voulez le comportement standard lorsque l'utilisateur appuie sur "Non".
ban-geoengineering

33

Ont modifié le code @ user919216 .. et l'ont rendu compatible avec WebView

@Override
public void onBackPressed() {
    if (webview.canGoBack()) {
        webview.goBack();

    }
    else
    {
     AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                finish();
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
AlertDialog alert = builder.create();
alert.show();
    }

}

22

Je préfère quitter avec un double tap sur le bouton de retour qu'avec une boîte de dialogue de sortie.

Dans cette solution, il affiche un toast lorsque vous revenez pour la première fois, avertissant qu'une autre pression arrière fermera l'application. Dans cet exemple, moins de 4 secondes.

private Toast toast;
private long lastBackPressTime = 0;

@Override
public void onBackPressed() {
  if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
    toast = Toast.makeText(this, "Press back again to close this app", 4000);
    toast.show();
    this.lastBackPressTime = System.currentTimeMillis();
  } else {
    if (toast != null) {
    toast.cancel();
  }
  super.onBackPressed();
 }
}

Jeton de: http://www.androiduipatterns.com/2011/03/back-button-behavior.html


Celui-ci est définitivement mon préféré car la boîte de dialogue peut être encombrante. Je l'ai légèrement changé pour le rendre 3 secondes et il semble un peu plus naturel avec ce marqueur. Merci pour le post.
PGMacDesign

sur double dos, idéalement, l'application devrait me quitter, mais dans mon code, il est ouvert une activité de connexion (signifie une activité de retour avec le spinner activé)
techDigi

21

Si vous n'êtes pas sûr si l'appel à "retour" quittera l'application ou mènera l'utilisateur à une autre activité, vous pouvez envelopper les réponses ci-dessus dans une vérification, isTaskRoot (). Cela peut se produire si votre activité principale peut être ajoutée plusieurs fois à la pile arrière, ou si vous manipulez l'historique de votre pile arrière.

if(isTaskRoot()) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                YourActivity.super.onBackPressed;
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
    AlertDialog alert = builder.create();
    alert.show();

} else {
    super.onBackPressed();
}

5

Utilisation de Lambda:

    new AlertDialog.Builder(this).setMessage(getString(R.string.exit_msg))
        .setTitle(getString(R.string.info))
        .setPositiveButton(getString(R.string.yes), (arg0, arg1) -> {
            moveTaskToBack(true);
            finish();
        })
        .setNegativeButton(getString(R.string.no), (arg0, arg1) -> {
        })
        .show();

Vous devez également définir un langage de niveau pour prendre en charge java 8 dans votre gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}

5

en Chine, la plupart des applications confirmeront la sortie en "cliquant deux fois":

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 

5

Retirez d'abord super.onBackPressed();de la onbackPressed()méthode le code ci-dessous:

@Override
public void onBackPressed() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    MyActivity.this.finish();
               }
           })
           .setNegativeButton("No", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
               }
           });
    AlertDialog alert = builder.create();
    alert.show();

}

2

J'aime une approche @GLee et je l'utilise avec un fragment comme ci-dessous.

@Override
public void onBackPressed() {
    if(isTaskRoot()) {
        new ExitDialogFragment().show(getSupportFragmentManager(), null);
    } else {
        super.onBackPressed();
    }
}

Dialogue utilisant Fragment:

public class ExitDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.exit_question)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getActivity().finish();
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getDialog().cancel();
                }
            })
            .create();
    }
}

2
Just put this code in your first activity 

@Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
            drawerLayout.closeDrawer(GravityCompat.END);
        }
        else {
// if your using fragment then you can do this way
            int fragments = getSupportFragmentManager().getBackStackEntryCount();
            if (fragments == 1) {
new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    finish();
               }
           })
           .setNegativeButton("No", null)
           .show();


            } else {
                if (getFragmentManager().getBackStackEntryCount() > 1) {
                    getFragmentManager().popBackStack();
                } else {

           super.onBackPressed();
                }
            }
        }
    }

y première activité, je ne peux pas le mettre en activité au milieu. parce que maintenant il ferme l'activité en cours et affiche la précédente
shareef

1

Une autre alternative serait d' afficher un Toast/ Snackbarlors de la première pression arrière en demandant à nouveau d'appuyer sur Quitter , ce qui est beaucoup moins intrusif que de montrer un AlertDialogpour confirmer si l'utilisateur veut quitter l'application.

Vous pouvez utiliser le DoubleBackPress Android Library pour y parvenir avec quelques lignes de code. Exemple GIF montrant un comportement similaire.

Pour commencer, ajoutez la dépendance à votre application:

dependencies {
    implementation 'com.github.kaushikthedeveloper:double-back-press:0.0.1'
}

Ensuite, dans votre activité, implémentez le comportement requis.

// set the Toast to be shown on FirstBackPress (ToastDisplay - builtin template)
// can be replaced by custom action (new FirstBackPressAction{...})
FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);

// set the Action on DoubleBackPress
DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
    @Override
    public void actionCall() {
        // TODO : Exit the application
        finish();
        System.exit(0);
    }
};

// setup DoubleBackPress behaviour : close the current Activity
DoubleBackPress doubleBackPress = new DoubleBackPress()
        .withDoublePressDuration(3000)     // msec - wait for second back press
        .withFirstBackPressAction(firstBackPressAction)
        .withDoubleBackPressAction(doubleBackPressAction);

Enfin, définissez-le comme comportement sur la presse arrière.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.