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

我在想,当我越来越频繁地看到它时,这是一个内置的功能,你可以在某个活动中访问它吗?我已经看了很多类的源代码,但我似乎找不到任何关于这一点。

当然,我可以想到一些很容易实现相同功能的方法(最简单的可能是在活动中保留一个布尔值,指示用户是否已经单击过一次…),但我想知道这里是否已经有一些东西。

编辑:正如@LAS_VEGAS所提到的,我并不是指传统意义上的“退出”。(即终止)我的意思是“回到应用程序启动活动启动之前打开的任何东西”,如果这有意义的话:)


当前回答

它不是内置的功能。我认为这甚至不是被推荐的行为。Android应用程序并不意味着退出:

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

其他回答

最近,我需要在我的一个应用程序中实现这个后退按钮功能。对最初问题的回答是有用的,但我必须考虑到另外两点:

在某些时间点,返回按钮被禁用 主要的活动是将片段与反向堆栈结合使用

根据回答和评论,我创建了以下代码:

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()替换getSupportFragmentManager()。

如果后退按钮从未取消,则删除第一个if。删除第二个if,如果你不使用片段或片段返回堆栈

另外,重要的是要知道onBackPressed方法从Android 2.0开始就被支持了。查看本页详细描述。为了使背按功能也适用于旧版本,将以下方法添加到您的活动中:

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

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

Zefnus使用System.currentTimeMillis()的答案是最好的(+1)。我的方法并没有比这更好,但仍然发布它来补充上面的想法。

如果后退按钮按下时吐司不可见,则显示吐司,反之,如果它可见(后退已经在最后一个吐司中按了一次。LENGTH_SHORT time),然后退出。

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

这个解的独特之处在于它的行为;其中,非双击将显示吐司和成功双击将显示没有吐司,同时关闭应用程序。 唯一的缺点是吐司的显示将有650毫秒的延迟。我相信这是最佳行为的最佳解决方案,因为逻辑表明,如果没有这样的延迟,就不可能有这种行为

//App Closing Vars
private var doubleBackPressedInterval: Long = 650
private var doubleTap = false
private var pressCount = 0
private var timeLimit: Long = 0

override fun onBackPressed() {
    pressCount++
    if(pressCount == 1) {
        timeLimit = System.currentTimeMillis() + doubleBackPressedInterval
        if(!doubleTap) {
            showExitInstructions()
        }
    }
    if(pressCount == 2) {
        if(timeLimit > System.currentTimeMillis()) {
            doubleTap = true
            super.onBackPressed()
        }
        else {
            showExitInstructions()
        }
        pressCount = 1
        timeLimit = System.currentTimeMillis() + doubleBackPressedInterval
    }
}

private fun showExitInstructions() {
    Handler().postDelayed({
        if(!doubleTap) {
            Toast.makeText(this, "Try Agian", Toast.LENGTH_SHORT).show()
        }
    }, doubleBackPressedInterval)
}

在这里,我已经概括编写了N个点击计数的代码。代码类似地为android设备中的启用开发人员选项编写。甚至你可以在开发人员测试应用程序时使用它来启用功能。

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

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

 }

在退按或注销选项时调用askToExit()。

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