Al hacer clic dos veces en el botón Atrás para salir de una actividad.

331

Recientemente noté este patrón en muchas aplicaciones y juegos de Android: al hacer clic en el botón Atrás para "salir" de la aplicación, Toastaparece un mensaje similar a "Haga clic en ATRÁS nuevamente para salir".

Me preguntaba, como lo veo cada vez más a menudo, ¿es esa una característica incorporada a la que puedes acceder de alguna manera en una actividad? He mirado el código fuente de muchas clases pero parece que no puedo encontrar nada al respecto.

Por supuesto, puedo pensar en algunas maneras de lograr la misma funcionalidad con bastante facilidad (la más fácil es mantener un valor booleano en la actividad que indica si el usuario ya hizo clic una vez ...) pero me preguntaba si ya hay algo aquí. .

EDITAR : Como @LAS_VEGAS mencionó, realmente no quise decir "salir" en el significado tradicional. (es decir, terminado) quise decir "volver a lo que estaba abierto antes de que se iniciara la actividad de inicio de la aplicación", si eso tiene sentido :)

Guillaume
fuente
[Android: confirmar la salida de la aplicación con tostadas] [1]: stackoverflow.com/questions/14006461/…
resource8218
1
Tuve el mismo problema al usar la biblioteca de HoloEverywhere, simplemente puede agregar android: launchMode = "singleTask" a su definición de actividad en el archivo de manifiesto.
Sohayb Hassoun
Otra solución stackoverflow.com/questions/8430805/…
Webserveis

Respuestas:

949

En la actividad de 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);
} 

En la actividad de 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)
    }

Creo que este controlador ayuda a restablecer la variable después de 2 segundos.

Sudheesh B Nair
fuente
43
¡La mejor respuesta! También puede agregar condición if (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount ()! = 0) {en caso de agregar basado en fragmentos
Anton Derevyanko
2
Estoy de acuerdo, esta es definitivamente la mejor respuesta y debería ser la respuesta aceptada.
BruceHill
3
Debe eliminar Runnable cuando salga de la aplicación.
Wayne
Verifique mi respuesta. He modificado la respuesta dada Sudheesh B Nairpara cubrir los comentarios sugeridos anteriormente.
Mehul Joisar
18
Es una buena solución / respuesta rápida, pero no estoy de acuerdo con que sea la mejor solución . Y para aquellos que piensan que esta será la mejor respuesta nuevamente, no puedo estar de acuerdo. Esta solución causa fugas y requerirá un esfuerzo adicional para su manejo. Consulte las respuestas a continuación para obtener más detalles.
Saro Taşciyan
226

Sudheesh B Nair tiene una respuesta agradable (y aceptada) sobre la pregunta, que creo que debería tener una mejor alternativa como;

¿Qué hay de malo en medir el tiempo transcurrido y verificar si TIME_INTERVALpasaron milisegundos (por ejemplo, 2000) desde la última vez que presionó hacia atrás? Se llama al siguiente código de muestra System.currentTimeMillis();para almacenar el tiempo onBackPressed();

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

Volver a la respuesta aceptada crítica ; El uso de a flagpara indicar si se presionó en los últimos TIME_INTERVALmilisegundos (por ejemplo, 2000) y se estableció el restablecimiento mediante Handlerel postDelayed()método fue lo primero que se me ocurrió. Pero la postDelayed()acción debe cancelarse cuando se cierra la actividad, eliminando el Runnable.

Para eliminarlo Runnable, no debe declararse anónimo , y debe declararse como miembro junto con el Handlertambién. Entonces removeCallbacks()método deHandler puede ser llamado apropiadamente.

La siguiente muestra es la demostración;

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

Gracias a @NSouth por contribuir; Para evitar que aparezcan mensajes de brindis incluso después de cerrar la aplicación, Toastpuede declararse como miembro, digamos mExitToast, y puede cancelarse mExitToast.cancel();justo antes de la super.onBackPressed();llamada.

Saro Taşciyan
fuente
99
Para aquellos que piensan que es lo mismo, Sudheesh B Nair dijo: Misma funcionalidad, mejor rendimiento. entonces +1.
BedirYilmaz
44
Me gusta esta respuesta y creo que es la mejor. Quiero decir que no lo creo, es la mejor respuesta, por las razones mencionadas anteriormente. Espero que obtengas más votos a favor para este. Sin embargo, un comentario: ¿a nadie le parece extraño que la tostada persista un par de segundos después de que se cierre la aplicación? ¿A nadie le importa cancelar la tostada? Sé que puede ser un pequeño detalle, pero creo que debería suceder. ¿Qué piensan ustedes?
acrespo 01 de
1
@joonty gracias por editar, esa palabra clave int ha estado ausente por algún tiempo. Se compilará ahora (:
Saro Taşciyan
2
El bloque de código @NSouth Second fue una muestra usando mHandlers para mostrar que requería más esfuerzo. Le sugiero que considere usar el primer bloque de código, que no usa un controlador.
Saro Taşciyan
1
@acrespo Supongo que tenemos una solución sólida para mensajes persistentes que persisten después de que la aplicación se cierra ahora.
Saro Taşciyan
30

Solo pensé en compartir cómo lo hice al final, agregué en mi actividad:

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

Y simplemente funciona exactamente como quiero. Incluyendo el restablecimiento del estado cada vez que se reanuda la actividad.

Guillaume
fuente
15
Con esta solución, las dos presiones anteriores pueden tener una cantidad arbitraria de tiempo entre ellas. Entonces puede presionar Backuna vez y luego presionar Backnuevamente un minuto después y la aplicación se cerrará. Este no es el comportamiento que el usuario esperará.
BruceHill
1
Creo que es cuestión de gustos. En este caso, notifica al usuario solo una vez, para que el usuario sepa que se encuentra en la actividad principal y otra presión hacia atrás saldrá de la aplicación (probablemente después de que el usuario haya presionado un par de veces para volver a la pantalla principal). Si, más tarde, el usuario vuelve a presionar, podemos suponer que quiere salir de la aplicación (a menos que haya navegado a otras actividades y probablemente haya perdido la noción de cuán profundo llegó). En la respuesta aceptada anterior, considera que el usuario puede olvidar que ya está en la pantalla principal. Ambos están bien, dependiendo de lo que quieras o consideres.
Ferran Maylinch
44
@FerranMaylinch - No estoy de acuerdo. Esto no es simplemente una cuestión de gustos. Si ha pasado un tiempo considerable, que debemos asumir que el usuario ha realizado otras acciones, mientras tanto, y ya no se considera lo que hizo anteriormente y optó por no continuar con la aplicación. De hecho, todos, excepto el usuario más excepcional, ni siquiera tendrán en su mente que lo hizo anteriormente. Sin un límite de tiempo, ha dejado la aplicación en un modo invisible que el usuario no tiene forma de conocer. Considero que es un diseño de interfaz de usuario deficiente. Los usuarios se sorprenderán.
ToolmakerSteve
En mi humilde opinión, mejores variantes de esto aquí y aquí .
ToolmakerSteve
26

Diagrama de flujo del proceso: Presione nuevamente para salir.

Código 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;
}
vCómo
fuente
1
Inicialmente voté esto como útil. El problema es que, por alguna razón, esto no funciona en algunos teléfonos. El método onBackPressed funciona mejor, pero no tiene las marcas de tiempo, por lo que necesita los controladores como indica la respuesta aceptada.
ravemir
23

Hay una manera muy simple entre todas estas respuestas.

Simplemente escriba el siguiente código dentro del onBackPressed()método.

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

Necesita definir el back_pressedobjeto como longen la actividad.

Chintan Rathod
fuente
16

Mi solución 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();
    }
}

Simple y con estilo.

Hugo Passos
fuente
2
Gracias por esta solución Simple, efectivo, sin riesgo.
ping li
2
Solución lista para usar. (aplauso)
Hitesh Dhamshaniya
13

Basado en la respuesta correcta y las sugerencias en los comentarios, he creado una demostración que funciona absolutamente bien y elimina las devoluciones de llamada del controlador después de ser utilizado.

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

}

Espero que sea util !!

Mehul Joisar
fuente
¿Está seguro de eliminar el controlador para onBackPressed()solucionar el problema de pérdida de memoria ?
Saro Taşciyan
@ Zefnus: Se solucionará hasta donde yo sé. Corrígeme si estoy equivocado. ¿Cómo rastreó el problema de memoria con el código anterior?
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);

Declarar variableprivate boolean doubleBackToExitPressedOnce = false;

Pegue esto en su Actividad principal y esto resolverá su problema

Rakshith
fuente
10

No es una buena idea usar un Runnable al salir de la aplicación, recientemente descubrí una forma mucho más simple de registrar y comparar el período entre dos clics en el botón ATRÁS. Código de muestra de la siguiente manera:

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

Esto hará el truco para salir de la aplicación haciendo doble clic en el botón ATRÁS dentro de un cierto período de retraso que es de 2000 milisegundos en la muestra.

Max Lee
fuente
8

La respuesta aceptada es la mejor, pero si está usando Android Design Support Library, puede usar SnackBarpara obtener mejores vistas.

   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
fuente
7

No es una funcionalidad incorporada. Creo que ni siquiera es el comportamiento recomendado. Las aplicaciones de Android no están destinadas a salir:

¿Por qué las aplicaciones de Android no ofrecen la opción "Salir"?

Caner
fuente
Punto a favor. Al salir, quise decir "volver a la pantalla de inicio"
Guillaume
3
Todavía no es una funcionalidad incorporada. Sin embargo, no conozco ninguna directriz en contra de esto. Como usuario de Android, me gusta esa funcionalidad.
Caner
6
  1. Declare una variable global Toast para la clase MainActivity. ejemplo: Toast exitToast;
  2. Inicialícelo en el método de vista onCreate. ejemplo: exitToast = Toast.makeText (getApplicationContext (), "Presione de nuevo para salir", Toast.LENGTH_SHORT);
  3. Finalmente, cree un método onBackPressed como sigue:

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

Esto funciona correctamente, lo he probado. Y creo que esto es mucho más simple.

Supto
fuente
5

La respuesta de Zefnus usando System.currentTimeMillis () es la mejor (+1). La forma en que lo hice es no mejor que eso, pero todavía publicarla en añadir a las ideas anteriores.

Si la tostada no es visible cuando se presiona el botón de retroceso, se muestra la tostada, mientras que si está visible (la última Toast.LENGTH_SHORTvez que se presionó la última vez), se cierra.

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
fuente
1
Es una buena manera de hacer esto, pero si tiene más mensajes de tostadas no lo es.
Boldijar Paul
@BoldijarPaul No, este código solo verifica el estado de ese brindis específico. Entonces, cualquier otra tostada no afectará el comportamiento.
urgente el
5

Recientemente, necesitaba implementar esta función de botón de retroceso en una aplicación mía. Las respuestas a la pregunta original fueron útiles, pero tuve que tener en cuenta dos puntos más:

  1. En algunos momentos, el botón de retroceso está desactivado
  2. La actividad principal es usar fragmentos en combinación con un back stack

Basado en las respuestas y comentarios, creé el siguiente código:

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

El código anterior supone que se utiliza la biblioteca de soporte. Si usa fragmentos pero no la biblioteca de soporte, desea reemplazarlos getSupportFragmentManager()por getFragmentManager().

Elimine el primero if, si el botón Atrás nunca se cancela. Quitar el segundoif , si no usa fragmentos o una pila de fragmentos

Además, es importante tener en cuenta que el método onBackPressedes compatible desde Android 2.0. Consulte esta página para obtener una descripción detallada. Para hacer que la función de retroceso funcione también en versiones anteriores, agregue el siguiente método a su actividad:

@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
fuente
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
fuente
4

Sé que esta es una pregunta muy antigua, pero esta es la forma más fácil de hacer lo que quieres.

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

Sé que este no es el mejor método, ¡pero funciona bien!

Chris Rohit Brendan
fuente
3
Este no es el comportamiento general de "hacer clic en el botón Atrás dos veces para salir". Al igual que el comentario de BruceHill sobre las respuestas aceptadas, su respuesta no maneja el problema del tiempo también
inankupeli
pero con esto puede hacer clic en Atrás, mostrará el mensaje, y luego puede esperar un poco más, volver de nuevo y cerrará la aplicación, y no manejará el comportamiento del tiempo de doble retroceso
Gastón Saillén
3

Para este propósito he implementado la siguiente función:

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();
}
Pero yo
fuente
3

Esto también ayuda cuando tiene actividad de pila anterior almacenada en la pila.

He modificado la respuesta 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
fuente
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
fuente
1
¿Cómo maneja esto el escenario de doble presión? Tan pronto como respondo, esto inicia una actividad.
kilokahn
2

Un método un poco mejor que Zefnus , creo. Llame a System.currentTimeMillis () solo una vez y omita 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
fuente
2

Aquí está el código de trabajo completo. Y tampoco olvide eliminar las devoluciones de llamada para que no se pierda memoria en la aplicación. :)

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
fuente
2

Aquí, he generalizado escribir el código para N recuentos de tomas. El código está escrito de manera similar para la opción Habilitar desarrollador en el teléfono del dispositivo Android. Incluso puede usar esto para habilitar funciones mientras el desarrollador prueba la aplicación.

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

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

 }

Llame a askToExit () en la opción de retroceso o cierre de sesión.

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
fuente
2

yo uso esto

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

}

utilizar en casoonBackPressed

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

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

para cambiar los segundos es necesario cerrar, milisegundos especificados

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

Webserveis
fuente
2

Cuando HomeActivity contiene el cajón de navegación y la función doble BackPressed () para salir de la aplicación. (No olvide iniciar la variable global booleana doubleBackToExitPressedOnce = false;) nuevo controlador después de 2 segundos establezca la variable doubleBackPressedOnce en 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
fuente
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
fuente
1

Algunas mejoras en la respuesta de Sudheesh B Nair, he notado que esperará al controlador incluso mientras presiona hacia atrás dos veces inmediatamente, así que cancele el controlador como se muestra a continuación. También he preparado tostadas para evitar que se muestre después de salir de la aplicación.

 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
fuente
1

Esta es la misma respuesta aceptada y más votada, pero esta recortada usó Snackbar en lugar 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
fuente
1

En mi caso, he dependido Snackbar#isShown()para mejorar 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
fuente
1

Para la actividad que tiene Navigation Drawer , use el siguiente código para 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
fuente
1

Aquí hay otra forma ... usando el método 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
fuente