单击后退按钮两次以退出活动

Gui*_*ume 311 java android back-button

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

我想知道,因为我越来越频繁地看到它是一个内置的功能,你可以以某种方式访问​​活动?我查看了许多类的源代码,但我似乎无法找到任何相关内容.

当然,我可以考虑几种方法来轻松实现相同的功能(最容易的是在活动中保留一个布尔值,表明用户是否已经点击过一次......)但是我想知道这里是否有东西.

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

小智 897

在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);
} 
Run Code Online (Sandbox Code Playgroud)

在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)
    }
Run Code Online (Sandbox Code Playgroud)

我认为这个处理程序有助于在2秒后重置变量.

  • 最佳答案!你也可以添加条件if(doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount()!= 0){基于片段的添加 (38认同)
  • 这是一个很好的快速解决方案/答案,但我不同意它是*最佳解决方案*.对于那些认为这将是*最佳答案*的人,我不能同意.这些解决方案导致**泄漏**并且需要额外的处理努力.查看下面的*aswers*了解更多详情. (15认同)
  • 退出应用程序时应删除Runnable. (3认同)
  • 我同意,这绝对是最好的答案,应该是公认的答案. (2认同)

Sar*_*yan 216

Sudheesh B Nair在这个问题上有一个很好的(并且被接受的)答案,我认为应该有更好的选择,例如;

测量时间过去并检查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();
}
Run Code Online (Sandbox Code Playgroud)

回到接受的答案批评 ; 使用flag以表明如果它是在最后压TIME_INTERVAL(比如2000年)毫秒和设置-复位是通过HandlerpostDelayed()方法是进入我脑海的第一件事.但是postDelayed()当活动结束时,应该取消操作,删除Runnable.

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

以下示例是演示;

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);
}
Run Code Online (Sandbox Code Playgroud)

感谢@NSouth的贡献; 为了防止即使在应用程序关闭后出现toast消息,Toast也可以声明为成员 - 比如说mExitToast- 并且可以mExitToast.cancel();super.onBackPressed();调用之前取消.

  • 对于那些认为与Sudheesh B Nair所说的相同的人:功能相同,性能更好.所以+1. (8认同)
  • 第一个代码块有最好的答案. (7认同)
  • 我喜欢这个答案,我认为这是最好的.我的意思是我不认为它是,因为上述原因,这是最好的答案.我希望你能得到更多的赞成.但有一条评论:没有人觉得应用程序结束后吐司会持续几秒钟,这有点奇怪吗?没人关心取消祝酒词?我知道这可能是一个小细节,但我认为应该发生.你们有什么感想? (4认同)
  • @NSouth第二个代码块是一个使用mHandlers的示例,以表明它需要更多的努力.我建议你考虑使用第一个代码块,它不使用处理程序. (2认同)

Gui*_*ume 29

我想我最后会分享我是如何做到的,我刚刚在我的活动中加入:

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();
}
Run Code Online (Sandbox Code Playgroud)

它完全符合我的要求.包括恢复活动时的状态重置.

  • 使用这种解决方案,两次背压可以在它们之间具有任意的时间量.所以你可以按一次"Back",然后在一分钟后再按"Back",应用程序将退出.这不是用户期望的行为. (13认同)
  • @FerranMaylinch - 我不同意.这不仅仅是品味问题.如果时间过了很长时间,我们*应该*假设用户在此期间已经做了其他操作,并且不再考虑他之前做过的事情**并选择不继续**申请.事实上,除了最优秀的用户之外,其他人甚至都不会想到他以前那样做过.没有时间限制,您已将app**置于用户无法知道的隐形模式**中.我绝对认为这是糟糕的用户界面设计.用户会感到惊讶. (3认同)

小智 26

流程图: 再按一次退出.

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;
}
Run Code Online (Sandbox Code Playgroud)


Chi*_*hod 22

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

只需在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();
}
Run Code Online (Sandbox Code Playgroud)

您需要将back_pressed对象定义为long活动.


Hug*_*sos 14

我使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

简约时尚.

  • 谢谢你的解决方案.简单,有效,没有风险. (2认同)
  • 开箱即用解决方案.(拍) (2认同)

Meh*_*sar 13

根据正确的答案和评论中的建议,我创建了一个完全正常的演示,并在使用后删除处理程序回调.

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

}
Run Code Online (Sandbox Code Playgroud)

我希望它会有所帮助!!


Rak*_*ith 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);
Run Code Online (Sandbox Code Playgroud)

声明变量private boolean doubleBackToExitPressedOnce = false;

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


小智 10

退出应用程序时使用Runnable不是一个好主意,我最近想出了一种更简单的方法来记录和比较两个BACK按钮点击之间的时间.示例代码如下:

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();
}
Run Code Online (Sandbox Code Playgroud)

这样就可以通过双重BACK按钮在一定的延迟时间内点击退出应用程序,该延迟时间为2000毫秒.


Pra*_*ani 8

接受的答案是最佳答案,但如果您正在使用,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);
    }
Run Code Online (Sandbox Code Playgroud)


Can*_*ner 7

它不是内置功能.我认为这甚至不是推荐的行为.Android应用无意退出:

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

  • 它仍然不是内置的功能.但是我不知道有任何针对此的指导方针.作为Android用户,我喜欢这样的功能. (3认同)

小智 6

  1. 为MainActivity类声明一个全局Toast变量.例如:Toast exitToast;
  2. 在onCreate视图方法中初始化它.例如:exitToast = Toast.makeText(getApplicationContext(),"再按一次退出",Toast.LENGTH_SHORT);
  3. 最后创建一个onBackPressedMethod作为Follows:

    @Override
    public void onBackPressed() {
    
        if (exitToast.getView().isShown()) {
            exitToast.cancel();
            finish();
        } else {
            exitToast.show();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

这个工作正常,我已经测试过.我认为这更简单.


Chr*_*dan 5

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

@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(); 
   }
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这不是“单击后退按钮两次退出”的一般行为。就像 BruceHill 对已接受答案的评论指出的那样,您的答案也不能处理时间问题 (3认同)

Kar*_*tik 5

Zefnus使用System.currentTimeMillis()的答案是最好的答案(+1)。我这样做的方式并没有比这更好,但是仍然将其发布以增加上述想法。

如果在按下后退按钮时看不见烤面包,则显示该烤面包,而在它可见的情况下(在上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();
   }
}
Run Code Online (Sandbox Code Playgroud)


Tom*_*yen 5

最近,我需要在我的应用程序中实现这个后退按钮功能.原始问题的答案很有用,但我还要考虑两点:

  1. 在某些时间点,后退按钮被禁用
  2. 主要活动是将碎片与后堆叠结合使用

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

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码假定使用了支持库.如果您使用片段但不使用支持库,则需要替换getSupportFragmentManager()getFragmentManager().

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);
}
Run Code Online (Sandbox Code Playgroud)


Pra*_*een 5

在java中

private Boolean exit = false; 

if (exit) {
onBackPressed(); 
}
Run Code Online (Sandbox Code Playgroud)
 @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);

    }
}
Run Code Online (Sandbox Code Playgroud)

在科特林

 private var exit = false

 if (exit) {
        onBackPressed()
         }
Run Code Online (Sandbox Code Playgroud)
 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)

        }
    }
Run Code Online (Sandbox Code Playgroud)