Cliquer deux fois sur le bouton Retour pour quitter une activité

331

J'ai récemment remarqué ce modèle dans de nombreuses applications et jeux Android: lorsque vous cliquez sur le bouton Précédent pour "quitter" l'application, Toastun message similaire à "Veuillez cliquer à nouveau sur RETOUR pour quitter" s'affiche.

Je me demandais, comme je le vois de plus en plus souvent, est-ce une fonctionnalité intégrée à laquelle vous pouvez accéder d'une manière ou d'une autre dans une activité? J'ai regardé le code source de nombreuses classes mais je n'arrive pas à trouver quoi que ce soit à ce sujet.

Bien sûr, je peux penser à quelques façons d'obtenir la même fonctionnalité assez facilement (la plus simple est probablement de garder un booléen dans l'activité qui indique si l'utilisateur a déjà cliqué une fois ...) mais je me demandais s'il y avait déjà quelque chose ici .

EDIT : Comme @LAS_VEGAS l'a mentionné, je ne voulais pas vraiment dire «sortie» au sens traditionnel. (c'est-à-dire terminé), je voulais dire "revenir à tout ce qui était ouvert avant le lancement de l'activité de démarrage de l'application", si cela a du sens :)

Guillaume
la source
[Android - Confirmer la sortie de l'application avec du pain grillé] [1]: stackoverflow.com/questions/14006461/…
resource8218
1
J'ai eu le même problème lors de l'utilisation de la bibliothèque HoloEverywhere, trop simplement, vous pouvez ajouter android: launchMode = "singleTask" à votre définition d'activité dans le fichier manifeste.
Sohayb Hassoun
Autre solution stackoverflow.com/questions/8430805/…
Webserveis

Réponses:

949

Dans l'activité Java:

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

Dans l'activité Kotlin:

private var doubleBackToExitPressedOnce = false
override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }

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

        Handler().postDelayed(Runnable { doubleBackToExitPressedOnce = false }, 2000)
    }

Je pense que ce gestionnaire permet de réinitialiser la variable après 2 secondes.

Sudheesh B Nair
la source
44
Meilleure réponse! Vous pouvez également ajouter une condition si (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount ()! = 0) {en cas d'ajout basé sur les fragments
Anton Derevyanko
2
Je suis d'accord, c'est certainement la meilleure réponse et devrait être la réponse acceptée.
BruceHill
3
Vous devez supprimer Runnable lorsque vous quittez l'application.
Wayne
Vérifiez ma réponse. J'ai modifié la réponse donnée par Sudheesh B Nairpour couvrir les commentaires suggérés ci-dessus.
Mehul Joisar
19
C'est une bonne solution / réponse rapide mais je ne suis pas d' accord pour dire que c'est la meilleure solution . Et pour ceux qui pensent que ce sera la meilleure réponse , je ne suis pas d'accord. Cette solution provoque des fuites et nécessitera des efforts supplémentaires pour la manipulation. Consultez les demandes ci-dessous pour plus de détails.
Saro Taşciyan
226

Sudheesh B Nair a une réponse agréable (et acceptée) à la question, qui je pense devrait avoir une meilleure alternative telle que;

Quel est le problème avec la mesure du temps écoulé et la vérification si les TIME_INTERVALmillisecondes (disons 2000) se sont écoulées depuis la dernière pression arrière. L'exemple de code suivant utilise System.currentTimeMillis();pour stocker l'heure onBackPressed()est appelé;

private static final int TIME_INTERVAL = 2000; // # milliseconds, desired time passed between two back presses.
private long mBackPressed;

@Override
public void onBackPressed()
{
    if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) 
    { 
        super.onBackPressed(); 
        return;
    }
    else { Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show(); }

    mBackPressed = System.currentTimeMillis();
}

Retour sur la critique des réponses acceptées ; Utiliser une flagpour indiquer si elle a été pressée au cours des dernières TIME_INTERVAL(disons 2000) millisecondes et set - reset est par Handlerla postDelayed()méthode de via a été la première chose qui m'est venue à l'esprit. Mais l' postDelayed()action doit être annulée à la fermeture de l'activité, supprimant le Runnable.

Afin de supprimer le Runnable, il ne doit pas être déclaré anonyme et être déclaré comme membre avec l' Handleraswell. Ensuite, la removeCallbacks()méthode de Handlerpeut être appelée de manière appropriée.

L'exemple suivant est la démonstration;

private boolean doubleBackToExitPressedOnce;
private Handler mHandler = new Handler();

private final Runnable mRunnable = new Runnable() {
    @Override
    public void run() {
        doubleBackToExitPressedOnce = false;                       
    }
};

@Override 
protected void onDestroy() 
{ 
    super.onDestroy();

    if (mHandler != null) { mHandler.removeCallbacks(mRunnable); }
}

@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();

    mHandler.postDelayed(mRunnable, 2000);
}

Merci à @NSouth pour sa contribution; Afin d'éviter que le message toast n'apparaisse même après la fermeture de l'application, il Toastpeut être déclaré membre - disons mExitToast- et peut être annulé via mExitToast.cancel();juste avant l' super.onBackPressed();appel.

Saro Taşciyan
la source
9
Pour ceux qui pensent que c'est pareil ce que Sudheesh B Nair a dit: Même fonctionnalité, meilleures performances. donc +1.
BedirYilmaz
4
J'aime cette réponse et je pense que c'est la meilleure. Je veux dire que je ne pense pas que ce soit, C'EST la meilleure réponse, pour les raisons indiquées ci-dessus. J'espère que vous obtiendrez plus de votes positifs pour celui-ci. Un commentaire cependant: personne ne trouve étrange que le toast persiste quelques secondes après la fermeture de l'application? Personne ne se soucie d'annuler le toast? Je sais que c'est peut-être un petit détail mais je pense que cela devrait arriver. Qu'en pensez-vous?
acrespo
1
@joonty merci pour la modification, ce mot clé int est absent depuis un certain temps. Il compilera maintenant (:
Saro Taşciyan
2
Le bloc de code @NSouth Second était un exemple utilisant mHandlers afin de montrer qu'il nécessitait plus d'efforts. Je vous suggère d'envisager d'utiliser le premier bloc de code, qui n'utilise pas de gestionnaire.
Saro Taşciyan
1
@acrespo Je suppose que nous avons une solution solide pour que le message toast persiste après la fermeture de l'application .
Saro Taşciyan
30

J'ai juste pensé que je partagerais comment je l'ai fait à la fin, j'ai juste ajouté dans mon activité:

private boolean doubleBackToExitPressedOnce = false;

@Override
protected void onResume() {
    super.onResume();
    // .... other stuff in my onResume ....
    this.doubleBackToExitPressedOnce = false;
}

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }
    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, R.string.exit_press_back_twice_message, Toast.LENGTH_SHORT).show();
}

Et cela fonctionne exactement comme je le souhaite. Y compris la réinitialisation de l'état à chaque reprise de l'activité.

Guillaume
la source
15
Avec cette solution, les deux contre-presses peuvent avoir un intervalle de temps arbitraire entre elles. Vous pouvez donc appuyer Backune fois puis appuyer à Backnouveau une minute plus tard et l'application se fermera. Ce n'est pas le comportement auquel s'attend l'utilisateur.
BruceHill
1
Je pense que c'est une question de goût. Dans ce cas, vous avertissez l'utilisateur une seule fois, afin que l'utilisateur sache qu'il est dans l'activité principale et une autre contre-pression quittera l'application (probablement après que l'utilisateur a appuyé plusieurs fois pour revenir à l'écran principal). Si, plus tard, l'utilisateur appuie à nouveau, nous pouvons supposer qu'il veut quitter l'application (à moins qu'il n'ait navigué dans d'autres activités et ait probablement perdu la trace de sa profondeur). Dans la réponse acceptée ci-dessus, vous considérez que l'utilisateur peut oublier qu'il est déjà dans l'écran principal. Les deux sont très bien, selon ce que vous voulez ou considérez.
Ferran Maylinch
4
@FerranMaylinch - Je ne suis pas d'accord. Ce n'est pas seulement une question de goût. Si un délai important s'est écoulé, nous devons supposer que l'utilisateur a effectué d'autres actions dans l'intervalle, et ne considère plus ce qu'il a fait précédemment et a choisi de ne pas continuer à postuler. En effet, tous, sauf l'utilisateur le plus exceptionnel, n'auront même pas encore en tête qu'il l'a déjà fait. Sans limite de temps, vous avez laissé l'application dans un mode invisible que l'utilisateur n'a aucun moyen de connaître. Je considère absolument que c'est une mauvaise conception de l'interface utilisateur. Les utilisateurs seront surpris.
ToolmakerSteve
À mon humble avis, de meilleures variantes à ce sujet ici et ici .
ToolmakerSteve
26

Diagramme de flux de processus: Appuyez à nouveau pour quitter.

Code Java:

private long lastPressedTime;
private static final int PERIOD = 2000;

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        switch (event.getAction()) {
        case KeyEvent.ACTION_DOWN:
            if (event.getDownTime() - lastPressedTime < PERIOD) {
                finish();
            } else {
                Toast.makeText(getApplicationContext(), "Press again to exit.",
                        Toast.LENGTH_SHORT).show();
                lastPressedTime = event.getEventTime();
            }
            return true;
        }
    }
    return false;
}
vComment
la source
1
Au départ, j'ai voté comme utile. Le problème est, pour une raison quelconque, que cela ne fonctionne pas sur certains combinés. La méthode onBackPressed fonctionne mieux, mais vous n'avez pas d'horodatage, vous avez donc besoin des gestionnaires comme les états de réponse acceptés.
ravemir
23

Il y a moyen le plus simple parmi toutes ces réponses.

Écrivez simplement le code suivant dans la onBackPressed()méthode.

long back_pressed;

@Override
public void onBackPressed() {
    if (back_pressed + 1000 > System.currentTimeMillis()){
        super.onBackPressed();
    }
    else{
        Toast.makeText(getBaseContext(),
                "Press once again to exit!", Toast.LENGTH_SHORT)
                .show();
    }
    back_pressed = System.currentTimeMillis();
}

Vous devez définir l' back_pressedobjet comme longen activité.

Chintan Rathod
la source
16

Ma solution avec snackbar:

Snackbar mSnackbar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final LinearLayout layout = findViewById(R.id.layout_main);
    mSnackbar = Snackbar.make(layout, R.string.press_back_again, Snackbar.LENGTH_SHORT);
}

@Override
public void onBackPressed() {
    if (mSnackbar.isShown()) {
        super.onBackPressed();
    } else {
        mSnackbar.show();
    }
}

Simple et élégant.

Hugo Passos
la source
2
Merci pour cette solution. Simple, efficace, sans risque.
ping li
2
Solution prête à l'emploi. (applaudissement)
Hitesh Dhamshaniya
13

Sur la base de la bonne réponse et des suggestions dans les commentaires, j'ai créé une démo qui fonctionne parfaitement et supprime les rappels du gestionnaire après avoir été utilisé.

MainActivity.java

package com.mehuljoisar.d_pressbacktwicetoexit;

import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.widget.Toast;

public class MainActivity extends Activity {

    private static final long delay = 2000L;
    private boolean mRecentlyBackPressed = false;
    private Handler mExitHandler = new Handler();
    private Runnable mExitRunnable = new Runnable() {

        @Override
        public void run() {
            mRecentlyBackPressed=false;   
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onBackPressed() {

        //You may also add condition if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount() != 0) // in case of Fragment-based add
        if (mRecentlyBackPressed) {
            mExitHandler.removeCallbacks(mExitRunnable);
            mExitHandler = null;
            super.onBackPressed();
        }
        else
        {
            mRecentlyBackPressed = true;
            Toast.makeText(this, "press again to exit", Toast.LENGTH_SHORT).show();
            mExitHandler.postDelayed(mExitRunnable, delay);
        }
    }

}

J'espère que ce sera utile !!

Mehul Joisar
la source
Êtes-vous certain de supprimer le gestionnaire pour onBackPressed()résoudre le problème de fuite de mémoire ?
Saro Taşciyan
@Zefnus: Cela corrigera pour autant que je sache. Corrigez-moi si je me trompe. Comment avez-vous suivi le problème de mémoire avec le code ci-dessus?
Mehul Joisar
11
 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);

Déclarer une variableprivate boolean doubleBackToExitPressedOnce = false;

Collez-le dans votre activité principale et cela résoudra votre problème

Rakshith
la source
10

Ce n'est pas une bonne idée d'utiliser un Runnable lorsque vous quittez l'application, j'ai récemment trouvé un moyen beaucoup plus simple d'enregistrer et de comparer la période entre deux clics sur le bouton RETOUR. Exemple de code comme suit:

private static long back_pressed_time;
private static long PERIOD = 2000;

@Override
public void onBackPressed()
{
        if (back_pressed_time + PERIOD > System.currentTimeMillis()) super.onBackPressed();
        else Toast.makeText(getBaseContext(), "Press once again to exit!", Toast.LENGTH_SHORT).show();
        back_pressed_time = System.currentTimeMillis();
}

Cela fera l'affaire pour quitter l'application par un double clic sur le bouton RETOUR dans un certain délai qui est de 2000 millisecondes dans l'échantillon.

Max Lee
la source
8

La réponse acceptée est la meilleure, mais si vous utilisez, Android Design Support Libraryvous pouvez utiliser SnackBarpour de meilleures vues.

   boolean doubleBackToExitPressedOnce = false;

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

        this.doubleBackToExitPressedOnce = true;

        Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

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

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }
Pratik Butani
la source
7

Ce n'est pas une fonctionnalité intégrée. Je pense que ce n'est même pas le comportement recommandé. Les applications Android ne sont pas censées quitter:

Pourquoi les applications Android ne proposent-elles pas l'option "Quitter"?

Caner
la source
Point pris. Par sortie, je voulais dire "revenir à l'écran d'accueil"
Guillaume
3
Ce n'est pas encore une fonctionnalité intégrée. Cependant, je ne connais aucune directive contre cela. En tant qu'utilisateur Android, j'aime une telle fonctionnalité.
Caner
6
  1. Déclarez une variable globale Toast pour la classe MainActivity. exemple: Toast exitToast;
  2. Initialisez-le dans la méthode de vue onCreate. exemple: exitToast = Toast.makeText (getApplicationContext (), "Appuyez de nouveau pour quitter", Toast.LENGTH_SHORT);
  3. Enfin, créez un onBackPressedMethod comme suit:

    @Override
    public void onBackPressed() {
    
        if (exitToast.getView().isShown()) {
            exitToast.cancel();
            finish();
        } else {
            exitToast.show();
        }
    }

Cela fonctionne correctement, j'ai testé. et je pense que c'est beaucoup plus simple.

Supto
la source
5

La réponse de Zefnus en utilisant System.currentTimeMillis () est la meilleure (+1). La façon dont je l'ai fait n'est pas meilleure que cela, mais la publie toujours pour ajouter aux idées ci-dessus.

Si le toast n'est pas visible lorsque vous appuyez sur le bouton de retour, le toast s'affiche, tandis que s'il est visible (le dos a déjà été pressé une fois au cours de la dernière Toast.LENGTH_SHORTfois), il se ferme.

exitToast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT);
.
.
@Override
public void onBackPressed() {
   if (exitToast.getView().getWindowToken() == null) //if toast is currently not visible
      exitToast.show();  //then show toast saying 'press againt to exit'
   else {                                            //if toast is visible then
      finish();                                      //or super.onBackPressed();
      exitToast.cancel();
   }
}
Kartik
la source
1
C'est un bon moyen de le faire, mais si vous avez plus de messages toast, ce n'est pas le cas.
Boldijar Paul
@BoldijarPaul Non, ce code vérifie uniquement l'état de ce toast spécifique. Tous les autres toasts n'affecteront donc pas le comportement.
urgentx
5

Récemment, j'ai dû implémenter cette fonction de bouton de retour dans une de mes applications. Les réponses à la question d'origine étaient utiles, mais je devais prendre en compte deux autres points:

  1. À certains moments, le bouton de retour est désactivé
  2. L'activité principale consiste à utiliser des fragments en combinaison avec une pile arrière

Sur la base des réponses et des commentaires, j'ai créé le code suivant:

private static final long BACK_PRESS_DELAY = 1000;

private boolean mBackPressCancelled = false;
private long mBackPressTimestamp;
private Toast mBackPressToast;

@Override
public void onBackPressed() {
    // Do nothing if the back button is disabled.
    if (!mBackPressCancelled) {
        // Pop fragment if the back stack is not empty.
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            super.onBackPressed();
        } else {
            if (mBackPressToast != null) {
                mBackPressToast.cancel();
            }

            long currentTimestamp = System.currentTimeMillis();

            if (currentTimestamp < mBackPressTimestamp + BACK_PRESS_DELAY) {
                super.onBackPressed();
            } else {
                mBackPressTimestamp = currentTimestamp;

                mBackPressToast = Toast.makeText(this, getString(R.string.warning_exit), Toast.LENGTH_SHORT);
                mBackPressToast.show();
            }
        }
    }
}

Le code ci-dessus suppose que la bibliothèque de support est utilisée. Si vous utilisez des fragments mais pas la bibliothèque de support, vous souhaitez remplacer getSupportFragmentManager()par getFragmentManager().

Retirez le premier if, si le bouton Retour n'est jamais annulé. Supprimez le second if, si vous n'utilisez pas de fragments ou une pile arrière de fragments

De plus, il est important de savoir que la méthode onBackPressedest prise en charge depuis Android 2.0. Consultez cette page pour une description détaillée. Pour que la fonction de contre-presse fonctionne également sur les anciennes versions, ajoutez la méthode suivante à votre activité:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)  {
    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR
            && keyCode == KeyEvent.KEYCODE_BACK
            && event.getRepeatCount() == 0) {
        // Take care of calling this method on earlier versions of
        // the platform where it doesn't exist.
        onBackPressed();
    }

    return super.onKeyDown(keyCode, event);
}
Tomasz Nguyen
la source
5

En java

private Boolean exit = false; 

if (exit) {
onBackPressed(); 
}

 @Override
public void onBackPressed() {
    if (exit) {
        finish(); // finish activity
    } else {
        Toast.makeText(this, "Press Back again to Exit.",
                Toast.LENGTH_SHORT).show();
        exit = true;
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                exit = false;
            }
        }, 3 * 1000);

    }
}

En kotlin

 private var exit = false

 if (exit) {
        onBackPressed()
         }

 override fun onBackPressed(){
           if (exit){
               finish() // finish activity
           }else{
            Toast.makeText(this, "Press Back again to Exit.",
                    Toast.LENGTH_SHORT).show()
            exit = true
            Handler().postDelayed({ exit = false }, 3 * 1000)

        }
    }
Praveen
la source
4

Je sais que c'est une très vieille question, mais c'est la façon la plus simple de faire ce que vous voulez.

@Override
public void onBackPressed() {
   ++k; //initialise k when you first start your activity.
   if(k==1){
      //do whatever you want to do on first click for example:
      Toast.makeText(this, "Press back one more time to exit", Toast.LENGTH_LONG).show();
   }else{
      //do whatever you want to do on the click after the first for example:
      finish(); 
   }
}

Je sais que ce n'est pas la meilleure méthode, mais ça marche bien!

Chris Rohit Brendan
la source
3
Ce n'est pas le comportement général de "cliquer deux fois sur le bouton de retour pour quitter". Comme le souligne le commentaire de BruceHill sur la réponse acceptée, votre réponse ne gère pas trop le problème de temps
inankupeli
mais avec cela, vous pouvez cliquer en arrière, il affichera le message, puis vous pouvez attendre un peu plus longtemps, revenir en arrière et fermer l'application, et il ne gérera pas le comportement de la temporisation double arrière
Gastón Saillén
3

À cet effet, j'ai mis en œuvre la fonction suivante:

private long onRecentBackPressedTime;
@Override
public void onBackPressed() {
    if (System.currentTimeMillis() - onRecentBackPressedTime > 2000) {
       onRecentBackPressedTime = System.currentTimeMillis();
       Toast.makeText(this, "Please press BACK again to exit", Toast.LENGTH_SHORT).show();
       return;
     }
   super.onBackPressed();
}
Mais je
la source
3

Cela aide également lorsque vous avez une activité de pile précédente stockée dans la pile.

J'ai modifié la réponse de Sudheesh

boolean doubleBackToExitPressedOnce = false;

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

  Intent intent = new Intent(Intent.ACTION_MAIN);
                    intent.addCategory(Intent.CATEGORY_HOME);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//***Change Here***
                    startActivity(intent);
                    finish();
                    System.exit(0);
        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);
} 
Arpit Patel
la source
2
@Override public void onBackPressed() {
   Log.d("CDA", "onBackPressed Called");
   Intent intent = new Intent();
   intent.setAction(Intent.ACTION_MAIN);
   intent.addCategory(Intent.CATEGORY_HOME);

   startActivity(intent);
}
Sai Aditya
la source
1
Comment cela gère-t-il même le scénario de double presse? Dès que je riposte, cela lance une activité.
kilokahn
2

Une méthode légèrement meilleure que Zefnus je pense. Appelez System.currentTimeMillis () une seule fois et omettez return;:

long previousTime;

@Override
public void onBackPressed()
{
    if (2000 + previousTime > (previousTime = System.currentTimeMillis())) 
    { 
        super.onBackPressed();
    } else {
        Toast.makeText(getBaseContext(), "Tap back button in order to exit", Toast.LENGTH_SHORT).show();
    }
}
k8C
la source
2

Voici le code de travail complet. Et n'oubliez pas de supprimer les rappels afin de ne pas provoquer de fuite de mémoire dans l'application. :)

private boolean backPressedOnce = false;
private Handler statusUpdateHandler;
private Runnable statusUpdateRunnable;

public void onBackPressed() {
        if (backPressedOnce) {
            finish();
        }

        backPressedOnce = true;
        final Toast toast = Toast.makeText(this, "Press again to exit", Toast.LENGTH_SHORT);
        toast.show();

        statusUpdateRunnable = new Runnable() {
            @Override
            public void run() {
                backPressedOnce = false;
                toast.cancel();  //Removes the toast after the exit.
            }
        };

        statusUpdateHandler.postDelayed(statusUpdateRunnable, 2000);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (statusUpdateHandler != null) {
        statusUpdateHandler.removeCallbacks(statusUpdateRunnable);
    }
}
Saket Kumar
la source
2

Ici, j'ai généralisé écrire le code pour N comptages de prises. Le code est également écrit pour l'option Activer le développeur dans le téléphone de l'appareil Android. Même vous pouvez l'utiliser pour activer les fonctionnalités pendant que le développeur teste l'application.

 private Handler tapHandler;
 private Runnable tapRunnable;
 private int mTapCount = 0;
 private int milSecDealy = 2000;

onCreate(){
 ...
tapHandler = new Handler(Looper.getMainLooper());

 }

Appelez askToExit () sur l'option backpress ou logout.

private void askToExit() {
   if (mTapCount >= 2) {
    releaseTapValues();
    /* ========= Exit = TRUE  =========  */
   }

   mTapCount++;
   validateTapCount();
  }


  /* Check with null to avoid create multiple instances of the runnable */
  private void validateTapCount() {
   if (tapRunnable == null) {
    tapRunnable = new Runnable() {
     @Override
     public void run() {
      releaseTapValues();
      /* ========= Exit = FALSE  =========  */
     }
    };
    tapHandler.postDelayed(tapRunnable, milSecDealy);
   }
  }

  private void releaseTapValues() {
   /* Relase the value  */
   if (tapHandler != null) {
    tapHandler.removeCallbacks(tapRunnable);
    tapRunnable = null; /* release the object */
    mTapCount = 0; /* release the value */
   }
  }


  @Override
  protected void onDestroy() {
   super.onDestroy();
   releaseTapValues();
  }
Chintan Khetiya
la source
2

J'utilise ceci

import android.app.Activity;
import android.support.annotation.StringRes;
import android.widget.Toast;

public class ExitApp {

    private static long lastClickTime;

    public static void now(Activity ctx, @StringRes int message) {
        now(ctx, ctx.getString(message), 2500);
    }

    public static void now(Activity ctx, @StringRes int message, long time) {
        now(ctx, ctx.getString(message), time);
    }

    public static void now(Activity ctx, String message, long time) {
        if (ctx != null && !message.isEmpty() && time != 0) {
            if (lastClickTime + time > System.currentTimeMillis()) {
                ctx.finish();
            } else {
                Toast.makeText(ctx, message, Toast.LENGTH_SHORT).show();
                lastClickTime = System.currentTimeMillis();
            }
        }
    }

}

utiliser en casonBackPressed

@Override
public void onBackPressed() {
   ExitApp.now(this,"Press again for close");
}

ou ExitApp.now(this,R.string.double_back_pressed)

pour changer les secondes, besoin de millisecondes proches et spécifiées

ExitApp.now(this,R.string.double_back_pressed,5000)

Webserveis
la source
2

Lorsque HomeActivity contient un tiroir de navigation et une double fonction backPressed () pour quitter l'application. (N'oubliez pas d'initialiser la variable globale booléenne doubleBackToExitPressedOnce = false;) nouveau gestionnaire après 2 secondes, définissez la variable doubleBackPressedOnce sur false

@Override
public void onBackPressed() {
    DrawerLayout drawer = findViewById(R.id.drawer_layout);
    if (drawer.isDrawerOpen(GravityCompat.END)) {
        drawer.closeDrawer(GravityCompat.END);
    } else {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed();
            moveTaskToBack(true);
            return;
        } else {
            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);
        }
    }
}
Abhishek S
la source
1
boolean doubleBackToExitPressedOnce = false;

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

    this.doubleBackToExitPressedOnce = true;

    Snackbar.make(findViewById(R.id.photo_album_parent_view), "Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

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

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);
}
maniix
la source
1

Quelques améliorations dans la réponse de Sudheesh B Nair, j'ai remarqué qu'il attendra le gestionnaire même en appuyant deux fois immédiatement, donc annulez le gestionnaire comme indiqué ci-dessous. J'ai également annulé le toast pour l'empêcher de s'afficher après la sortie de l'application.

 boolean doubleBackToExitPressedOnce = false;
        Handler myHandler;
        Runnable myRunnable;
        Toast myToast;

    @Override
        public void onBackPressed() {
            if (doubleBackToExitPressedOnce) {
                myHandler.removeCallbacks(myRunnable);
                myToast.cancel();
                super.onBackPressed();
                return;
            }

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

            myHandler = new Handler();

            myRunnable = new Runnable() {

                @Override
                public void run() {
                    doubleBackToExitPressedOnce = false;
                }
            };
            myHandler.postDelayed(myRunnable, 2000);
        }
Divyang Panchal
la source
1

C'est la même chose que la réponse acceptée et la plus votée, mais cette Snackbar coupée utilisée au lieu de Toast.

boolean doubleBackToExitPressedOnce = false;

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

        this.doubleBackToExitPressedOnce = true;
        Snackbar.make(content, "Please click BACK again to exit", Snackbar.LENGTH_SHORT)
                .setAction("Action", null).show();


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

            @Override
            public void run() {
                doubleBackToExitPressedOnce=false;
            }
        }, 2000);
    }
MobileOS
la source
1

Dans mon cas, je compte sur Snackbar#isShown()pour mieux UX.

private Snackbar exitSnackBar;

@Override
public void onBackPressed() {
    if (isNavDrawerOpen()) {
        closeNavDrawer();
    } else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        if (exitSnackBar != null && exitSnackBar.isShown()) {
            super.onBackPressed();
        } else {
            exitSnackBar = Snackbar.make(
                    binding.getRoot(),
                    R.string.navigation_exit,
                    2000
            );
            exitSnackBar.show();
        }
    } else {
        super.onBackPressed();
    }
}
Kasim Rangwala
la source
1

Pour l'activité dont le tiroir de navigation est utilisé, utilisez le code suivant pour OnBackPressed ()

boolean doubleBackToExitPressedOnce = false;

@Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            if (doubleBackToExitPressedOnce) {
                if (getFragmentManager().getBackStackEntryCount() ==0) {
                    finishAffinity();
                    System.exit(0);
                } else {
                    getFragmentManager().popBackStackImmediate();
                }
                return;
            }

            if (getFragmentManager().getBackStackEntryCount() ==0) {
                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);
            } else {
                getFragmentManager().popBackStackImmediate();
            }
        }
    }
Priya Rajan
la source
1

Voici une autre façon ... d'utiliser la méthode CountDownTimer

private boolean exit = false;
@Override
public void onBackPressed() {
        if (exit) {
            finish();
        } else {
            Toast.makeText(this, "Press back again to exit",
                    Toast.LENGTH_SHORT).show();
            exit = true;
            new CountDownTimer(3000,1000) {

                @Override
                public void onTick(long l) {

                }

                @Override
                public void onFinish() {
                    exit = false;
                }
            }.start();
        }

    }
Anuj Sain
la source