关于java:单击后退按钮两次以退出活动

Clicking the back button twice to exit an activity

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

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

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

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


In Java Activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}

In Kotlin Activity:

1
2
3
4
5
6
7
8
9
10
11
12
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)
    }

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


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

测量时间过去并检查自上次反压后是否超过TIME_INTERVAL毫秒(比如说2000),这有什么问题。以下示例代码使用System.currentTimeMillis();来存储调用onBackPressed()的时间;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()方法是我想到的第一件事。但是,当活动关闭时,应取消postDelayed()操作,删除Runnable

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

以下示例是演示;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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消息,Toast可以声明为成员 - 比如mExitToast - 并且可以在super.onBackPressed();调用之前通过mExitToast.cancel();取消。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
}

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


流程图:
Press again to exit.

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
}


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}

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


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

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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);
        }
    }

}

我希望它会有所帮助!!


我使用snackbar的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
    }
}

简约时尚。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 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);

声明变量private boolean doubleBackToExitPressedOnce = false;

Paste this in your Main Activity and this will solve your issue


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

1
2
3
4
5
6
7
8
9
10
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();
}

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


接受的答案是最佳答案,但如果您使用Android Design Support Library,则可以使用SnackBar获取更好的视图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   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);
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Override
    public void onBackPressed() {

        if (exitToast.getView().isShown()) {
            exitToast.cancel();
            finish();
        } else {
            exitToast.show();
        }
    }
  • 这个工作正常,我已经测试过。我认为这更简单。


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

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


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

    如果按下后退按钮时看不到吐司,则显示吐司,而如果可见(背面已在最后Toast.LENGTH_SHORT时间内按过一次),则退出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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();
       }
    }


    在java中

    1
    2
    3
    4
    5
    private Boolean exit = false;

    if (exit) {
    onBackPressed();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     @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);

        }
    }

    在kotlin

    1
    2
    3
    4
    5
     private var exit = false

     if (exit) {
            onBackPressed()
             }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     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)

            }
        }


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

  • 在某些时间点,后退按钮被禁用
  • 主要活动是将碎片与后堆叠结合使用
  • 根据答案和评论,我创建了以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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();
                }
            }
        }
    }

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

    如果永远不会取消后退按钮,请删除第一个if。如果您不使用片段或片段后栈,请删除第二个if

    此外,重要的是要注意自Android 2.0以来支持onBackPressed方法。请查看此页面以获取详细说明。要使背压功能也适用于旧版本,请将以下方法添加到您的活动中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @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);
    }

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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();
       }
    }

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


    为此,我实现了以下功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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();
    }

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

    我修改了Sudheesh的答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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);
    }

    1
    2
    3
    4
    5
    6
    7
    8
    @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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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);
        }
    }

    当HomeActivity包含导航抽屉和双backPressed()功能退出应用程序时。
    (不要忘记初始化全局变量boolean doubleBackToExitPressedOnce = false;)
    2秒后新的处理程序将doubleBackPressedOnce变量设置为false

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @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);
            }
        }
    }

    在这里,我已经概括地编写了N个抽头计数的代码。该代码类似于Android设备手机中的Enable Developer选项编写。即使您可以在开发人员测试应用程序时使用此功能来启用功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     private Handler tapHandler;
     private Runnable tapRunnable;
     private int mTapCount = 0;
     private int milSecDealy = 2000;

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

     }

    在backpress或logout选项上调用askToExit()。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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();
      }

    这与已接受和最多投票的回复相同,但是这个剪辑使用了Snackbar而不是Toast。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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);
        }

    我通常会添加评论,但我的声誉不允许这样做。
    所以这是我的两分钱:

    在Kotlin中,您可以使用协同程序将设置延迟为false:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    private var doubleBackPressed = false
    private var toast : Toast ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        toast = Toast.maketext(this,"Press back again to exit", Toast.LENGTH_SHORT)
    }

    override fun onBackPressed() {
        if (doubleBackPressed) {
            toast?.cancel()
            super.onBackPressed()
            return
        }
        this.doubleBackPressed = true
        toast?.show()
        GlobalScope.launch {
            delay(2000)
            doubleBackPressed = false
        }
    }

    你必须导入:

    1
    2
    3
    import kotlinx.coroutines.launch
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.GlobalScope


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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);
    }

    这是另一种方法......使用CountDownTimer方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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();
            }

        }

    我用这个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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();
                }
            }
        }

    }

    在事件onBackPressed中使用

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

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

    for change需要close,指定的毫秒数

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


    在Sudheesh B Nair的答案中有一些改进,我注意到它会等待处理程序,即使在立即按下两次,所以取消处理程序,如下所示。我也有tocled toast以防止它在app退出后显示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     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()一次并省略return;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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()使用以下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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();
                }
            }
        }

    就我而言,我依靠Snackbar#isShown()来获得更好的UX

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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();
        }
    }

    在这种情况下,Snackbar是更好的选择,然后Toast显示退出操作。以下是使用snackbar的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Override
            public void onBackPressed() {
                if (doubleBackToExitPressedOnce) {
                    super.onBackPressed();
                    return;
                }
                this.doubleBackToExitPressedOnce = true;
                Snackbar.make(this.getWindow().getDecorView().findViewById(android.R.id.content),"Please click BACK again to exit", Snackbar.LENGTH_SHORT).show();

                new Handler().postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        doubleBackToExitPressedOnce=false;
                    }
                }, 2000);
            }

    Toast的最佳解决方案

    In Java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private Toast exitToast;

    @Override
    public void onBackPressed() {
        if (exitToast == null || exitToast.getView() == null || exitToast.getView().getWindowToken() == null) {
            exitToast = Toast.makeText(this,"Press again to exit", Toast.LENGTH_LONG);
            exitToast.show();
        } else {
            exitToast.cancel();
            super.onBackPressed();
        }
    }

    In Kotlin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private var exitToast: Toast? = null

    override fun onBackPressed() {
        if (exitToast == null || exitToast!!.view == null || exitToast!!.view.windowToken == null) {
            exitToast = Toast.makeText(this,"Press again to exit", Toast.LENGTH_LONG)
            exitToast!!.show()
        } else {
            exitToast!!.cancel()
            super.onBackPressed()
        }
    }


    按下按钮2次后返回

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public void click(View view){
        if (isBackActivated) {
            this.finish();
        }
        if (!isBackActivated) {
            isBackActivated = true;
            Toast.makeText(getApplicationContext(),"Again", Toast.LENGTH_SHORT).show();
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    isBackActivated = false;  // setting isBackActivated after 2 second
                }
            }, 2000);
        }

    }


    以下是使用RxJava执行此操作的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    override fun onCreate(...) {
        backPresses.timeInterval(TimeUnit.MILLISECONDS, Schedulers.io())
                .skip(1) //Skip initial event; delay will be 0.
                .onMain()
                .subscribe {
                    if (it.time() < 7000) super.onBackPressed() //7000 is the duration of a Toast with length LENGTH_LONG.
                }.addTo(compositeDisposable)

        backPresses.throttleFirst(7000, TimeUnit.MILLISECONDS, Schedulers.io())
                .subscribe { Toast.makeText(this,"Press back again to exit.", LENGTH_LONG).show() }
                .addTo(compositeDisposable)
    }

    override fun onBackPressed() = backPresses.onNext(Unit)

    这是我对此的看法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     int oddeven = 0;
     long backBtnPressed1;
     long backBtnPressed2;
     @Override
     public void onBackPressed() {
         oddeven++;
         if(oddeven%2==0){
             backBtnPressed2 = System.currentTimeMillis();
             if(backBtnPressed2-backBtnPressed1<2000) {
                super.onBackPressed();
                return;
             }
         }
         else if(oddeven%2==1) {
             backBtnPressed1 = System.currentTimeMillis();    
            //  Insert toast back button here
         }
     }


    在Kotlin的背面按退出应用程序,您可以使用:

    定义一个全局变量:

    1
    private var doubleBackToExitPressedOnce = false

    覆盖onBackPressed:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    override fun onBackPressed() {
            if (doubleBackToExitPressedOnce) {
                super.onBackPressed()
                return
            }

            doubleBackToExitPressedOnce = true
            Toast.makeText(this,"Please click BACK again to exit", Toast.LENGTH_LONG).show()

            Handler().postDelayed({
                doubleBackToExitPressedOnce = false;
            }, 2000)
        }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    private static final int TIME_INTERVAL = 2000;
    private long mBackPressed;
        @Override
            public void onBackPressed() {

                if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) {
                    super.onBackPressed();
                    Intent intent = new Intent(FirstpageActivity.this,
                            HomepageActivity.class);
                    startActivity(intent);
                    finish();

                    return;
                } else {

                    Toast.makeText(getBaseContext(),
                           "Tap back button twice  to go Home.", Toast.LENGTH_SHORT)
                            .show();

                    mBackPressed = System.currentTimeMillis();

                }

            }

    您还可以使用Toast的可见性,因此您不需要Handler / postDelayed超解决方案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    Toast doubleBackButtonToast;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        doubleBackButtonToast = Toast.makeText(this,"Double tap back to exit.", Toast.LENGTH_SHORT);
    }

    @Override
    public void onBackPressed() {
        if (doubleBackButtonToast.getView().isShown()) {
            super.onBackPressed();
        }

        doubleBackButtonToast.show();
    }

    你甚至可以让它变得更简单,而且不使用手枪,只做这个=)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Long firstClick = 1L;
    Long secondClick = 0L;

    @Override
    public void onBackPressed() {
    secondClick = System.currentTimeMillis();
        if ((secondClick - firstClick) / 1000 < 2) {
              super.onBackPressed();
        } else {
              firstClick = System.currentTimeMillis();
              Toast.makeText(MainActivity.this,"click BACK again to exit", Toast.LENGTH_SHORT).show();
            }
     }

    在不得不多次实现相同的事情之后,决定它的时间有人建立一个简单易用的库。那是DoubleBackPress Android libraryREADME解释了随示例提供的所有API(如ToastDisplay + Exit Activity),但这里的步骤简要介绍。

    要开始,首先将dependency添加到您的应用程序:

    1
    2
    3
    dependencies {
        implementation 'com.github.kaushikthedeveloper:double-back-press:0.0.1'
    }

    接下来,在Activity中创建一个DoubleBackPress对象,该对象提供所需的行为。

    1
    2
    DoubleBackPress doubleBackPress = new DoubleBackPress();
    doubleBackPress.setDoublePressDuration(3000);           // msec

    然后创建一个需要在First Back Press上显示的Toast。在这里,您可以创建自己的Toast,或者使用library中提供的Standard Toast。通过后面的选项在这里这样做。

    1
    2
    FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);
    doubleBackPress.setFirstBackPressAction(firstBackPressAction);   // set the action

    现在,定义第二次背压发生时会发生什么。在这里,我们正在关闭活动。

    1
    2
    3
    4
    5
    6
    7
    DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
        @Override
        public void actionCall() {
            finish();
            System.exit(0);
        }
    };

    最后,使用DoubleBackPress行为覆盖您的背压行为。

    1
    2
    3
    4
    @Override
    public void onBackPressed() {
        doubleBackPress.onBackPressed();
    }

    具有类似行为要求的示例GIF


    我认为这就是你所需要的,我的意思是当我们想要展示这个toast时,当堆栈中只有一个活动并且用户从堆栈的最后一个活动回来时。

    1
    var exitOpened=false // Declare it globaly

    并在onBackPressed方法中将其更改为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    override fun onBackPressed() {
            if (isTaskRoot && !exitOpened)
            {
                exitOpened=true
                toast("Please press back again to exit")
                return
            }
            super.onBackPressed()
        }

    这里,如果当前活动是堆栈的根活动(第一个活动),则isTaskRoot将返回true,否则返回false。

    你可以在这里查看官方文档


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     private static final int TIME_DELAY = 2000;
        private static long back_pressed;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
        @Override
        public void onBackPressed() {
            if (back_pressed + TIME_DELAY > System.currentTimeMillis()) {
                super.onBackPressed();
            } else {
                Toast.makeText(getBaseContext(),"Press once again to exit!",
                        Toast.LENGTH_SHORT).show();
            }
            back_pressed = System.currentTimeMillis();
        }