Fare clic due volte sul pulsante Indietro per uscire da un’attività

Ho notato questo schema in molte app e giochi Android di recente: quando si fa clic sul pulsante Indietro per “uscire” dall’applicazione, un Toast visualizza un messaggio simile a “Clicca di nuovo su INDIETRO per uscire”.

Mi stavo chiedendo, come sto vedendo sempre più spesso, una funzionalità incorporata che puoi in qualche modo accedere ad un’attività? Ho esaminato il codice sorgente di molte classi ma non riesco a trovare nulla a riguardo.

Certo, posso pensare a qualche modo per ottenere la stessa funzionalità abbastanza facilmente (il più semplice è probabilmente mantenere un booleano nell’attività che indica se l’utente ha già cliccato una volta …) ma mi stavo chiedendo se c’è già qualcosa qui .

EDIT : come accennato @ LAS_VEGAS, non intendevo realmente “exit” nel significato tradizionale. (cioè terminato) Intendevo “tornare a quello che era aperto prima dell’avvio dell’attività di avvio dell’applicazione”, se questo ha senso 🙂

    Nell’attività 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); } 

    Nell’attività 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) } 

    Penso che questo gestore aiuti a resettare la variabile dopo 2 secondi.

    Sudheesh B Nair ha una bella (e accettata) risposta alla domanda, che penso dovrebbe avere un’alternativa migliore come;

    Cosa c’è di sbagliato nel misurare il tempo passato e controllare se i TIME_INTERVAL (ad esempio 2000) sono passati dall’ultima stampa indietro. Il seguente codice di esempio utilizza System.currentTimeMillis(); per memorizzare il tempo onBackPressed() viene chiamato;

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

    Ritornare a una critica di risposta accettata; Usando un flag per indicare se è stato premuto nell’ultimo TIME_INTERVAL (ad esempio 2000) millisecondi e set – reset è tramite il metodo postDelayed() Handler è stata la prima cosa che mi è venuta in mente. Ma l’azione postDelayed() dovrebbe essere annullata quando l’attività si sta chiudendo, rimuovendo il Runnable .

    Per rimuovere il Runnable , non deve essere dichiarato anonimo ed essere dichiarato come membro insieme al Handler . Quindi il metodo removeCallbacks() di Handler può essere chiamato appropriatamente.

    Il seguente esempio è la dimostrazione;

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

    Grazie a @NSouth per aver contribuito; Per evitare che il messaggio di toast appaia anche dopo la chiusura dell’applicazione, Toast può essere dichiarato membro – ad esempio mExitToast – e può essere cancellato tramite mExitToast.cancel(); appena prima di super.onBackPressed(); chiamata.

    Ho solo pensato di condividere come l’ho fatto alla fine, ho appena aggiunto alla mia attività:

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

    E funziona esattamente come voglio io. Compreso il reset dello stato ogni volta che l’attività viene ripresa.

    Diagramma di stream del processo: Premere di nuovo per uscire.

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

    C’è un modo molto semplice tra tutte queste risposte.

    Basta scrivere il seguente codice all’interno del metodo onBackPressed() .

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

    È necessario definire object back_pressed per un long di attività long .

    Sulla base della risposta corretta e dei suggerimenti nei commenti, ho creato una demo che funziona perfettamente e rimuove i callback del gestore dopo l’uso.

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

    Spero sarà utile !!

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

    Dichiara la variabile private boolean doubleBackToExitPressedOnce = false;

    Incolla questo nella tua attività principale e questo risolverà il tuo problema

    `

    Non è una buona idea usare un Runnable quando esci dall’applicazione, di recente ho trovato un modo molto più semplice per registrare e confrontare il periodo tra due clic sul pulsante BACK. Codice di esempio come segue:

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

    Questo farà il trucco per uscire dall’applicazione con un doppio tasto BACK in un determinato intervallo di tempo che è di 2000 millisecondi nel campione.

    La mia soluzione con lo snack bar:

     LinearLayout mLayout; Snackbar mSnackbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.layout_main); mSnackbar = Snackbar.make(mLayout, R.string.press_back_again, Snackbar.LENGTH_SHORT); } @Override public void onBackPressed() { if (mSnackbar.isShown()) { super.onBackPressed(); } else { mSnackbar.show(); } } 

    Semplice ed elegante.

    Non è una funzionalità integrata. Penso che non sia nemmeno il comportamento consigliato. Le app Android non sono destinate ad uscire:

    Perché le applicazioni Android non offrono un’opzione “Esci”?

    La risposta accettata è la migliore, ma se utilizzi la Android Design Support Library puoi utilizzare SnackBar per visualizzazioni migliori.

      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); } 
    1. Dichiarare una variabile Toast globale per la class MainActivity. esempio: Toast exitToast;
    2. Inizializzalo nel metodo di visualizzazione onCreate. esempio: exitToast = Toast.makeText (getApplicationContext (), “Premi nuovamente per uscire”, Toast.LENGTH_SHORT);
    3. Infine crea un metodo onBackPressedMethod come segue:

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

    Funziona correttamente, ho provato. e penso che questo sia molto più semplice.

    La risposta di Zefnus usando System.currentTimeMillis () è la migliore (+1). Il modo in cui l’ho fatto non è migliore di quello, ma lo sto ancora postando per aggiungere le idee di cui sopra.

    Se il brindisi non è visibile quando viene premuto il pulsante Indietro, viene visualizzato il brindisi, mentre, se è visibile (è già stato premuto il pulsante indietro una volta nell’ultimo tempo Toast.LENGTH_SHORT ), quindi si chiude.

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

    Recentemente, avevo bisogno di implementare questa funzionalità del pulsante Indietro in una mia app. Le risposte alla domanda originale sono state utili, ma ho dovuto prendere in considerazione altri due punti:

    1. Ad un certo punto nel tempo, il pulsante Indietro è disabilitato
    2. L’attività principale consiste nell’utilizzare i frammenti in combinazione con uno stack posteriore

    In base alle risposte e ai commenti, ho creato il seguente codice:

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

    Il codice sopra presuppone che venga utilizzata la libreria di supporto. Se si utilizzano i frammenti ma non la libreria di supporto, si desidera sostituire getSupportFragmentManager() con getFragmentManager() .

    Rimuovi il primo if , se il pulsante Indietro non viene mai annullato. Rimuovi il secondo if , se non usi frammenti o un gruppo di frammenti

    Inoltre, è importante sapere che il metodo onBackPressed è supportato da Android 2.0. Controlla questa pagina per una descrizione dettagliata. Per fare in modo che la funzionalità di ripresa posteriore funzioni anche su versioni precedenti, aggiungi il seguente metodo alla tua attività:

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

    So che questa è una domanda molto vecchia, ma questo è il modo più semplice per fare ciò che vuoi.

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

    So che questo non è il metodo migliore, ma funziona bene!

    Questo aiuta anche quando hai una pila di attività precedente memorizzata nello stack.

    Ho modificato la risposta di 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); } 
     @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); } 

    Per questo scopo ho implementato la seguente funzione:

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

    Ecco il codice completo funzionante. Inoltre, non dimenticare di rimuovere i callback in modo che non provochino perdite di memoria nell’app. 🙂

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

    Qui, ho generalizzato scrivere il codice per N numero di tap. Il codice è scritto in modo simile per l’opzione Abilita sviluppatore nel telefono del dispositivo Android. Anche tu puoi usarlo per abilitare le funzionalità mentre lo sviluppatore testa l’app.

      private Handler tapHandler; private Runnable tapRunnable; private int mTapCount = 0; private int milSecDealy = 2000; onCreate(){ ... tapHandler = new Handler(Looper.getMainLooper()); } 

    Chiama askToExit () sull’opzione backpress o 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(); } 

    Quando HomeActivity contiene la barra di navigazione e il doppio pulsante Indietro () funtion per uscire dall’app. (Non dimenticare di inizializzare la variabile globale boolean doubleBackToExitPressedOnce = false;) nuovo gestore dopo 2 secondi impostare la variabile doubleBackPressedOnce su 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); } } } 
     @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); } } 
     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); } 

    Alcuni miglioramenti nella risposta di Sudheesh B Nair, ho notato che attenderà il gestore anche se si preme indietro due volte immediatamente, quindi annulla il gestore come mostrato di seguito. Ho tostato anche per evitare che venga visualizzato dopo l’uscita dell’app.

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

    Un metodo leggermente migliore di Zefnus, penso. Chiama System.currentTimeMillis () solo una volta e ometti 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(); } } 

    Questo è lo stesso della risposta accettata e più votata, ma questo Snackbar usato è stato usato al posto di 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); } 

    Nel mio caso, sono dipendente da Snackbar#isShown() per una migliore 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(); } } 

    Per l’attività di cui si dispone di Navigation Drawer , utilizzare il seguente codice per 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(); } } } 

    Io uso questo

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

    utilizzare in evento onBackPressed

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

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

    per i secondi di modifica è necessario per alcuni, determinati millisecondi

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

      private static final int TIME_DELAY = 2000; private static long back_pressed; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onBackPressed() { if (back_pressed + TIME_DELAY > System.currentTimeMillis()) { super.onBackPressed(); } else { Toast.makeText(getBaseContext(), "Press once again to exit!", Toast.LENGTH_SHORT).show(); } back_pressed = System.currentTimeMillis(); }