studio regresar programar interfaz gui grafica boton atras anterior activity java android back-button

java - regresar - Haciendo clic dos veces en el botón Atrás para salir de una actividad



programar boton atras android studio (30)

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.

He notado este patrón en muchas aplicaciones y juegos de Android recientemente: al hacer clic en el botón Atrás para "salir" de la aplicación, aparece un mensaje similar a "Por favor, haga clic en VOLVER para salir".

Me preguntaba, como lo veo cada vez más a menudo, ¿es una característica integrada 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 formas de lograr la misma funcionalidad con bastante facilidad (lo más fácil es probablemente mantener un booleano en la actividad que indique si el usuario ya hizo clic una vez ...) pero me preguntaba si ya hay algo aquí. .

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


  1. Declare una variable global de Toast para la clase MainActivity. ejemplo: Toast exitToast;
  2. Inicialízalo en el método de visualización onCreate. ejemplo: exitToast = Toast.makeText (getApplicationContext (), "Presione atrás nuevamente 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.


Algunas mejoras en la respuesta de Sudheesh B Nair, me he dado cuenta de que esperará a un manejador, incluso al presionar dos veces inmediatamente, así que cancele el manejador como se muestra a continuación. También he cerrado la tostada 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); }


Aquí está el código de trabajo completo. Y tampoco olvide eliminar las devoluciones de llamada para que no se produzca una pérdida de 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); } }


Aquí hay otra manera ... 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(); } }


Aquí, he generalizado escribir el código para N conteos de tap. El código está escrito de manera similar para la opción Habilitar desarrollador en el teléfono con dispositivo Android. Incluso puedes 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(); }


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


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


En mi caso, dependo de Snackbar#isShown() para una mejor Snackbar#isShown() .

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


Este es el mismo de la respuesta aceptada y más votada, pero este Snackbar utilizado 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); }


Esto también ayuda cuando tienes actividad de pila previa 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); }


Hay una forma muy simple entre todas estas respuestas.

Simplemente escriba el siguiente código dentro del método 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(); }

Es necesario definir el objeto back_pressed long actividad.


La respuesta aceptada es la mejor, pero si está utilizando la Android Design Support Library , puede usar SnackBar para 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); }


La respuesta de Zefnus usando System.currentTimeMillis () es la mejor (+1). La forma en que lo hice no es mejor que eso, pero aún así lo publico para agregar a las ideas anteriores.

Si no se ve el brindis cuando se presiona el botón Atrás, se muestra el brindis, mientras que, si está visible (ya se ha pulsado Toast.LENGTH_SHORT una vez dentro de la última vez que se Toast.LENGTH_SHORT ), 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(); } }


Mi solución utilizando snackbar:

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

Sencillo y con estilo.


No es una buena idea usar un Runnable al salir de la aplicación, recientemente he descubierto una forma mucho más sencilla de grabar 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.



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


Para la actividad que tiene el cajón de navegación , 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(); } } }


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

  1. En algunos momentos, el botón Atrás está deshabilitado.
  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 asume que se usa la biblioteca de soporte. Si utiliza fragmentos, pero no la biblioteca de soporte, desea reemplazar getSupportFragmentManager() por getFragmentManager() .

Retire el primero if , si el botón de retroceso nunca se cancela. Quita el segundo if , si no usas fragmentos o una pila de fragmentos.

Además, es importante tener en cuenta que el método onBackPressed es compatible desde Android 2.0. Revise esta página para una descripción detallada. Para hacer que la función de presión trasera 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); }


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!


Sobre la base de 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 su 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); } } }

Espero que sea util !!


Solo pensé que compartiría cómo lo hice al final, acabo de agregar 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 yo quiero. Incluyendo el reinicio del estado cada vez que se reanude la actividad.


Un método ligeramente mejor que Zefnus , creo. Llame a System.currentTimeMillis () solo una vez y omita el 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(); } }


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 el evento onBackPressed

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

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

para los segundos de cambio se necesitan milisegundos cercanos y especificados

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


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

¿Qué hay de malo en medir el tiempo transcurrido y comprobar si TIME_INTERVAL milisegundos TIME_INTERVAL (por ejemplo, 2000) desde la última vez que se presionó? El siguiente código de ejemplo utiliza System.currentTimeMillis(); para almacenar la hora se llama 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(); }

De vuelta en la crítica de respuesta aceptada; Usar una TIME_INTERVAL para indicar si se presionó en los últimos TIME_INTERVAL (por ejemplo, 2000) y establecer - reiniciar es a través del método postDelayed() Handler fue lo primero que me vino a la mente. Pero la acción postDelayed() debe cancelarse cuando la actividad se está cerrando, eliminando el Runnable .

Para eliminar el Runnable , no debe declararse anónimo , y debe declararse como miembro junto con el Handler también. Luego, el método removeCallbacks() de Handler 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 aparezca el mensaje de brindis incluso después de que se cierre la aplicación, puede declararse como miembro (por ejemplo, mExitToast ) y puede cancelarse a través de mExitToast.cancel(); justo antes de super.onBackPressed(); llamada.


Diagrama de flujo del proceso:

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


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


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

Declare Variable private boolean doubleBackToExitPressedOnce = false;

Pega esto en tu actividad principal y esto resolverá tu problema


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