单击后退button两次以退出活动

最近我在很多Android应用程序和游戏中注意到了这种模式:当点击后退button退出应用程序时, Toast会出现类似于“请再次单击BACK退出”的消息。

当我越来越频繁地看到它时,我在想,是否可以通过某种方式访问​​活动中的内置function? 我看了很多类的源代码,但我似乎无法find任何有关的。

当然,我可以考虑几个方法来轻松实现相同的function(最简单的方法是在活动中保留一个布尔值,以指示用户是否已经点击过一次…),但是我想知道是否已经有一些东西在这里。

编辑 :作为@LAS_VEGAS提到,我并不是真正的意思是“退出”的传统意义。 (即终止)我的意思是“回到开始应用程序开始活动之前的任何事情”,如果这是有道理的:)

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

我认为这个处理程序有助于在2秒后重置该variables。

Sudheesh B Nair在这个问题上有一个很好的(也是可以接受的)答案,我认为应该有一个更好的select,比如:

测量时间已经过去了,并且检查自上次后按以来TIME_INTERVAL毫秒(如2000)是否已经过去了。 以下示例代码使用System.currentTimeMillis(); 存储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(); } 

回到接受的回答批评 ; 使用一个flag来指示它是否在上一次TIME_INTERVAL (例如2000)毫秒中被按下,并且通过HandlerpostDelayed()方法设置 – 重置是我首先想到的。 但是当活动closures时, postDelayed()操作应该被取消,移除Runnable

为了移除Runnable ,它不能被声明为匿名 ,并且与Handler一起被声明为成员。 然后,可以适当地调用Handler removeCallbacks()方法。

下面的示例是示范;

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

感谢@NSouth的贡献; 为了防止Toast消息在应用程序closures后出现, Toast可以被声明为一个成员 – 比如mExitToast – 并且可以通过mExitToast.cancel();来取消mExitToast.cancel(); 只是在super.onBackPressed();之前super.onBackPressed(); 呼叫。

只是以为我会分享我到底做了些什么,我只是在我的活动中join了:

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

它只是正如我所愿。 包括每当活动恢复时重置状态。

工艺stream程图: 再按一次退出。

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

基于正确的答案和意见build议,我创build了一个绝对正常的演示,并在使用后删除处理程序callback。

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

我希望它会有帮助!

所有这些答案中有最简单的方法。

只需在onBackPressed()方法中写下以下代码。

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

您只需要在活动中定义back_pressed对象。

在退出应用程序时使用Runnable并不是一个好主意,我最近想出了一个更简单的方法来logging和比较两个BACKbutton点击之间的时间间隔。 示例代码如下所示:

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

这样做会在样本中以2000毫秒的某个延迟时间内通过双击BACKbutton来退出应用程序。

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

声明variables private boolean doubleBackToExitPressedOnce = false;

将其粘贴到您的主要活动中,这将解决您的问题

`

这不是一个内置的function。 我认为这甚至不是推荐的行为。 Android应用程序不打算退出:

为什么Android应用程序不提供“退出”选项?

我知道这是一个非常古老的问题,但这是做你想做的最简单的方法。

 @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); }else{ //do whatever you want to do on the click after the first for example: finish(); } } 

我知道这不是最好的方法,但它工作正常!

Zefnus使用System.currentTimeMillis()的答案是最好的(+1)。 我做的方式并不比这个更好,但是仍然发布它来增加上述想法。

如果在按下后退button时不显示吐司,则显示吐司,而如果可见(在最后的Toast.LENGTH_SHORT时间内已经按下了一次),则退出。

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

最近,我需要在我的一个应用程序中实现这个后退buttonfunction。 原始问题的答案是有用的,但是我必须考虑两点:

  1. 在某些时候,后退button被禁用
  2. 主要的活动是使用碎片结合后退堆栈

根据答案和评论,我创build了以下代码:

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

上面的代码假定使用支持库。 如果使用片段而不是支持库,则需要使用getFragmentManager()replacegetSupportFragmentManager() getFragmentManager()

删除第一个,如果后退button从未取消。 如果不使用碎片或碎片堆栈,则删除第二个碎片

另外,请注意自Android 2.0以来支持onBackPressed的方法是很重要的。 检查这个页面详细的描述。 为了使旧版本的function可以工作,请将以下方法添加到您的活动中:

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

接受的答案是最好的,但如果你使用Android Design Support Library那么你可以使用SnackBar更好的意见。

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

我的解决scheme使用小吃棒:

 LinearLayout layout; Snackbar snackbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); layout = (LinearLayout) findViewById(R.id.layout_main); snackbar = snackbar.make(layout, R.string.press_back_again, Snackbar.LENGTH_LONG); } @Override public void onBackPressed() { if (snackbar.isShown()) { super.onBackPressed(); } else { snackbar.show(); } } 
 @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. 声明MainActivity类的全局Toastvariables。 例如:Toast exitToast;
  2. 在onCreate视图方法中初始化它。 例如:exitToast = Toast.makeText(getApplicationContext(),“再次按退出”,Toast.LENGTH_SHORT);
  3. 最后创build一个onBackPressedMethod如下:

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

这工作正常,我已经testing。 我觉得这简单得多。

为此我已经实现了以下function:

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

这是完整的工作代码。 也不要忘了删除callback,以免在应用程序中造成内存泄漏。 🙂

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

Sudheesh B Nair的一些改进的答案,我已经注意到它会等待处理程序,即使在立即按两次,所以取消处理程序如下所示。 我也忍受烤面包,以防止它在应用程序退出后显示。

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

当您将先前的堆栈活动存储在堆栈中时,这也有帮助。

我修改了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); } 

这是接受和最多投票反应相同,但这削减了使用小吃,而不是吐司。

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

就我而言,我依靠Snackbar#isShown()来获得更好的用户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(); } } 
 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); } 

我认为比Zefnus稍微好一点的方法。 调用System.currentTimeMillis()一次,省略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(); } } 

对于具有导航抽屉的活动,请对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(); } } } 
  int back = 1; @Override public void onBackPressed(){ if(back==2) { super.onBackPressed(); } else{ Toast.makeText(this,"Press back again to close",Toast.LENGTH_SHORT).show(); back++; } } 

如果你想简单而不搞乱复杂的代码,试试这个方法。