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


331

Ho notato questo schema in molte app e giochi Android di recente: quando si fa clic sul pulsante Indietro per "uscire" dall'applicazione, Toastviene visualizzato un messaggio simile a "Fare di nuovo clic su INDIETRO per uscire".

Mi chiedevo, visto che lo vedo sempre più spesso, è una funzionalità integrata a cui puoi accedere in qualche modo in un'attività? Ho esaminato il codice sorgente di molte classi ma non riesco a trovare nulla al riguardo.

Certo, posso pensare ad alcuni modi per ottenere la stessa funzionalità abbastanza facilmente (il più semplice è probabilmente mantenere un valore booleano nell'attività che indica se l'utente ha già fatto clic una volta ...) ma mi chiedevo se c'è già qualcosa qui .

EDIT : Come menzionato da @LAS_VEGAS, non intendevo veramente "uscita" nel significato tradizionale. (cioè terminato) intendevo "tornare a tutto ciò che era aperto prima dell'avvio dell'attività di avvio dell'applicazione", se ciò ha senso :)


[Android - Conferma l'uscita dell'app con toast] [1]: stackoverflow.com/questions/14006461/…
resource8218

1
Ho avuto lo stesso problema quando ho usato la libreria HoloEverywhere, troppo semplicemente puoi aggiungere android: launchMode = "singleTask" alla tua definizione di attività nel file manifest.
Sohayb Hassoun,


Risposte:


949

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à di 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 ripristinare la variabile dopo 2 secondi.


43
Migliore risposta! Puoi anche aggiungere la condizione if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount ()! = 0) {in caso di aggiunta basata su frammenti
Anton Derevyanko,

2
Sono d'accordo, questa è sicuramente la risposta migliore e dovrebbe essere la risposta accettata.
BruceHill,

3
Rimuovere Runnable all'uscita dall'applicazione.
Wayne,

Controlla la mia risposta. Ho modificato la risposta fornita da Sudheesh B Nairper coprire i commenti sopra suggeriti.
Mehul Joisar,

18
È una buona soluzione / risposta rapida ma non sono d'accordo che sia la soluzione migliore . E per quelli che pensano che questa sia la risposta migliore , non posso essere d'accordo. Questa soluzione provoca perdite e richiederà uno sforzo extra per la gestione. Controlla le risposte sotto per ulteriori dettagli.
Saro Taşciyan,

226

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

Cosa c'è che non va nel misurare il tempo trascorso e nel controllare se i TIME_INTERVALmillisecondi (diciamo 2000) sono passati dall'ultima stampa di ritorno. Il seguente codice di esempio utilizza System.currentTimeMillis();per memorizzare l'ora onBackPressed()viene chiamata;

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

Torna alla critica della risposta accettata ; Usare a flagper indicare se è stato premuto negli ultimi TIME_INTERVAL(diciamo 2000) millisecondi e il set - reset è tramite Handleril postDelayed()metodo è stata la prima cosa che mi è venuta in mente. Ma l' postDelayed()azione dovrebbe essere annullata quando l'attività si sta chiudendo, rimuovendo il Runnable.

Per rimuovere il Runnable, non deve essere dichiarato anonimo e dichiarato come membro insieme Handler. Quindi il removeCallbacks()metodo di Handlerpuò essere chiamato in modo appropriato.

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 il contributo; Al fine di evitare che appaiano messaggi di brindisi anche dopo la chiusura dell'applicazione, Toastpossono essere dichiarati come membri - diciamo mExitToast- e possono essere cancellati mExitToast.cancel();appena prima della super.onBackPressed();chiamata.


9
Per quelli che pensano che sia lo stesso ciò che ha detto Sudheesh B Nair: stessa funzionalità, prestazioni migliori. quindi +1.
BedirYilmaz,

4
Mi piace questa risposta e penso che sia la migliore. Voglio dire, non PENSO che lo sia, È LA risposta migliore, per i motivi sopra indicati. Spero che otterrai più voti per questo. Un commento però: nessuno trova strano che il toast persista un paio di secondi dopo la chiusura dell'app? A nessuno importa cancellare il brindisi? So che potrebbe essere un piccolo dettaglio, ma penso che dovrebbe accadere. Che cosa ne pensate?
acrespo,

1
@joonty grazie per la modifica, quella parola chiave int è mancata da qualche tempo. Ora verrà compilato (:
Saro Taşciyan il

2
@NSouth Il secondo blocco di codice è stato un esempio usando mHandlers per mostrare che richiedeva più sforzo. Ti suggerisco di prendere in considerazione l'utilizzo del primo blocco di codice, che non utilizza un gestore.
Saro Taşciyan,

1
@acrespo Suppongo che abbiamo una soluzione solida per il persistere del messaggio toast dopo la chiusura dell'applicazione ora.
Saro Taşciyan,

30

Ho pensato di condividere il modo in cui l'ho fatto alla fine, ho appena aggiunto la 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. Compreso il ripristino dello stato ogni volta che l'attività viene ripresa.


15
Con questa soluzione le due presse posteriori possono avere una quantità arbitraria di tempo tra di loro. Quindi puoi premere Backuna volta e poi premere di Backnuovo un minuto dopo e l'applicazione verrà chiusa. Questo non è il comportamento che l'utente si aspetta.
BruceHill,

1
Penso che sia una questione di gusti. In questo caso, l'utente viene avvisato una sola volta, quindi l'utente sa di essere nell'attività principale e un'altra pressione arretrata uscirà dall'app (probabilmente dopo che l'utente ha premuto un paio di volte per tornare alla schermata principale). Se, in seguito, l'utente preme di nuovo indietro, possiamo supporre che voglia uscire dall'app (a meno che non abbia navigato in un'altra attività e probabilmente abbia perso la traccia della profondità). Nella risposta accettata sopra, si considera che l'utente potrebbe dimenticare che si trova già nella schermata principale. Entrambi vanno bene, a seconda di cosa vuoi o consideri.
Ferran Maylinch,

4
@FerranMaylinch - Non sono d'accordo. Questa non è solo una questione di gusti. Se è trascorso un tempo significativo, dovremmo supporre che l'utente abbia fatto altre azioni nel frattempo e non consideri più ciò che ha fatto in precedenza e abbia scelto di non continuare ad applicare. In effetti, tutti tranne l'utente più eccezionale non avranno ancora in mente di averlo fatto in precedenza. Senza un limite di tempo, hai lasciato l'app in una modalità invisibile che l'utente non ha modo di conoscere. Ritengo assolutamente che sia una cattiva progettazione dell'interfaccia utente. Gli utenti saranno sorpresi.
ToolmakerSteve,

IMHO, migliori varianti su questo qui e qui .
ToolmakerSteve,

26

Diagramma di flusso 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;
}

1
Ho inizialmente votato come utile. Il problema è, per qualche motivo, che non funziona su alcuni portatili. Il metodo onBackPressed funziona meglio, ma in questo caso non hai i timestamp, quindi hai bisogno dei gestori come indica la risposta accettata.
ravemir,

23

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

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

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 l' back_pressedoggetto come longin attività.


16

La mia soluzione usando 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();
    }
}

Semplice ed elegante.


2
Grazie per questa soluzione Semplice, efficace, senza rischi.
ping li

2
Soluzione pronta all'uso. (applauso)
Hitesh Dhamshaniya,

13

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

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 possa essere utile !!


Rimozione sicura del gestore per onBackPressed()risolvere il problema di perdita di memoria ?
Saro Taşciyan l'

@Zefnus: risolverà per quanto ne so. Correggimi se sbaglio. Come hai riscontrato il problema di memoria con il codice sopra?
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);

Dichiara variabileprivate boolean doubleBackToExitPressedOnce = false;

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


10

Non è una buona idea usare un Runnable quando si esce dall'applicazione, di recente ho scoperto un modo molto più semplice per registrare e confrontare il periodo tra due clic del pulsante INDIETRO. 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();
}

Ciò consentirà di uscire dall'applicazione facendo doppio clic sul pulsante INDIETRO entro un certo periodo di ritardo, che è di 2000 millisecondi nel campione.


8

La risposta accettata è la migliore, ma se lo stai usando Android Design Support Library, puoi usarla SnackBarper Better Views.

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

7

Non è una funzionalità integrata. Penso che non sia nemmeno il comportamento raccomandato. Le app Android non sono pensate per uscire:

Perché le applicazioni Android non forniscono un'opzione "Esci"?


Punto preso. Per uscita intendevo "tornare alla schermata principale"
Guillaume

3
Non è ancora una funzionalità integrata. Tuttavia non sono a conoscenza di alcuna linea guida contro questo. Come utente Android, mi piace tale funzionalità.
Caner,

6
  1. Dichiarare una variabile Toast globale per la classe 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 onBackPressed come segue:

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

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


5

La risposta di Zefnus usando System.currentTimeMillis () è la migliore (+1). Il modo in cui l'ho fatto è non è migliore di quella, ma ancora la pubblicazione per aggiungere alle idee di cui sopra.

Se il toast non è visibile quando si preme il pulsante Indietro, viene visualizzato il toast, mentre, se è visibile (il back è già stato premuto una volta nell'ultima Toast.LENGTH_SHORTvolta), quindi esce.

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

1
È un buon modo per farlo, ma se hai più toast i messaggi non lo sono.
Boldijar Paul,

@BoldijarPaul No, questo codice controlla solo lo stato di quel brindisi specifico. Quindi qualsiasi altro brindisi non influirà sul comportamento.
urgentx,

5

Di recente, ho dovuto implementare questa funzione 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. In alcuni momenti, il pulsante Indietro è disabilitato
  2. L'attività principale sta usando i frammenti in combinazione con uno stack posteriore

Sulla base delle risposte e dei 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 frammenti ma non la libreria di supporto, si desidera sostituire getSupportFragmentManager()con getFragmentManager().

Rimuovere il primo if, se il pulsante Indietro non viene mai annullato. Rimuovi il secondo if, se non usi frammenti o una pila di frammenti

Inoltre, è importante essere consapevoli del fatto che il metodo onBackPressedè supportato da Android 2.0. Controlla questa pagina per una descrizione elaborata. Per far funzionare la funzione di stampa posteriore 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);
}

5

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

    }
}

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

        }
    }

4

So che questa è una domanda molto vecchia, ma questo è il modo più semplice per fare quello 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 benissimo!


3
Questo non è il comportamento generale di "fare clic due volte sul pulsante Indietro per uscire". Come sottolinea il commento di BruceHill sulla risposta accettata, anche la tua risposta non gestisce il problema del tempo
inankupeli,

ma con questo puoi fare clic indietro, mostrerà il messaggio, quindi puoi aspettare ancora un po ', tornare indietro e chiuderà l'app, e non gestirà il comportamento del doppio back timing
Gastón Saillén

3

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

3

Questo aiuta anche quando hai attività di pila precedente memorizzate in pila.

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

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

1
In che modo gestisce anche lo scenario della doppia stampa? Non appena rispondo, questo avvia un'attività.
Kilokahn,

2

Penso che sia un metodo leggermente migliore di Zefnus . 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();
    }
}

2

Ecco il codice di lavoro completo. Inoltre, non dimenticare di rimuovere i callback in modo che non causino una perdita 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);
    }
}

2

Qui, ho generalizzato scrivere il codice per N conteggi di tocchi. 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 gli sviluppatori testano 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();
  }

2

Io lo uso

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

}

usare per in casoonBackPressed

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

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

per i secondi di cambio sono necessari millisecondi vicini e specifici

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


2

Quando HomeActivity contiene il riquadro di navigazione e la doppia funzione BackPress () per uscire dall'app. (Non dimenticare di inizializzare la variabile globale booleana doubleBackToExitPressedOnce = false;) nuovo gestore dopo 2 secondi impostare 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);
        }
    }
}

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

1

Alcuni miglioramenti nella risposta di Sudheesh B Nair, ho notato che attenderà il gestore anche premendo due volte immediatamente indietro, quindi annulla il gestore come mostrato di seguito. Ho anche creato un toast per impedirne la visualizzazione dopo l'uscita dall'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);
        }

1

Questa è la stessa della risposta accettata e più votata, ma questo Snackbar usato spezzato invece 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);
    }

1

Nel mio caso, dipendo in Snackbar#isShown()meglio 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();
    }
}

1

Per l'attività per la quale è presente il cassetto di navigazione , utilizzare il codice seguente 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();
            }
        }
    }

1

Ecco un altro modo ... usando il metodo 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();
        }

    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.