Клацніть кнопку "назад", щоб вийти із дії


330

Я помітив цю схему в багатьох програмах та іграх для Android останнім часом: при натисканні кнопки "назад" для "виходу" з програми Toastз'являється повідомлення, подібне до "Будь ласка, натисніть НАЗАД знову для виходу".

Мені було цікаво, адже я все частіше і частіше бачу це вбудована функція, до якої можна якось отримати доступ у діяльності? Я переглянув вихідний код багатьох класів, але, здається, нічого про це не можу знайти.

Звичайно, я можу подумати про декілька способів досягти тієї ж функціональності досить легко (найпростіше, мабуть, тримати булеву активність, яка вказує на те, чи користувач уже натиснув один раз ...), але мені було цікаво, чи є щось вже тут .

EDIT : Як згадував @LAS_VEGAS, я насправді не мав на увазі "вихід" у традиційному значенні. (тобто припиняється) Я мав на увазі "повернутися до того, що було відкрито до запуску роботи програми", якщо це має сенс :)


[Android - підтвердження виходу програми за допомогою тостів] [1]: stackoverflow.com/questions/14006461/…
resource8218

1
У мене була така ж проблема при використанні бібліотеки HoloEverywhere, занадто просто ви можете додати андроїд: запускMode = "singleTask" до вас визначення активності у файлі маніфесту.
Sohayb Hassoun


1
Можливий
повтор

Відповіді:


947

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

У діяльності Котліна:

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 секунди.


43
Найкраща відповідь! Ви також можете додати умову, якщо (doubleBackToExitPressOnce || fragmentManager.getBackStackEntryCount ()! = 0) {у випадку додавання на основі фрагмента
Антон Дерев’янко

2
Я згоден, це, безумовно, найкраща відповідь і повинна бути прийнятою відповіддю.
BruceHill

3
Ви повинні видалити Runnable під час виходу із програми.
Уейн

Перевірте мою відповідь. Я змінив відповідь, надану Sudheesh B Nairдля висвітлення запропонованих коментарів.
Мехул Джойсар

18
Це приємне швидке рішення / відповідь, але я не згоден, що це найкраще рішення . І для тих, хто думає, що це буде найкращою відповіддю знову, я не можу погодитися. Цей розчин викликає протікання і потребує додаткових зусиль для поводження. Перегляньте нижче сітки, щоб отримати докладнішу інформацію.
Saro Taşciyan

226

Sudheesh B Nair 's має гарну (і прийняту) відповідь на питання, яке, на мою думку, повинно мати кращу альтернативу, таку як;

Що не так у вимірюванні пройденого часу та перевірці, чи минули 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) мілісекунди і набір - скидання здійснюється через Handler«S postDelayed()метод був першим , що спадає на мій погляд. Але postDelayed()дію слід скасувати, коли діяльність закривається, видаляючи Runnable.

Для того, щоб видалити його Runnable, він не повинен бути оголошений анонімним , а оголосити його членом разом із Handlerавелером. Тоді 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);
}

Дякуємо @NSouth за внесок; Щоб запобігти появі тост-повідомлення навіть після закриття програми, Toastйого можна оголосити учасником - скажімо mExitToast- і можна скасувати mExitToast.cancel();безпосередньо перед super.onBackPressed();викликом.


9
Для тих, хто думає, що це те саме, що сказав Sudheesh B Nair: Однакова функціональність, кращі показники. тому +1.
BedirYilmaz

4
Мені подобається ця відповідь, і я думаю, що це найкраще. Я маю на увазі, що я не думаю, що це найкраща відповідь з причин, зазначених вище. Я сподіваюся, що ви отримаєте більше результатів для цього. Хоча один коментар: нікому не здається дивним, що тост зберігається через пару секунд після закриття програми? Ніхто не дбає скасувати тост? Я знаю, що це може бути невелика деталь, але я думаю, що це має статися. Як ви думаєте, хлопці?
acrespo

1
@joonty дякую за редагування, що ключове слово int вже відсутнє. Він
збиратиметься

2
@NSouth Другий блок коду був вибіркою з використанням mHandlers для того, щоб показати, що потрібно більше зусиль. Я пропоную вам розглянути можливість використання першого блоку коду, який не використовує обробник.
Saro Taşciyan

1
@acrespo Я вважаю, що у нас надійне рішення для тостів, що зберігається після закриття програми .
Саро Ташцян

30

Просто думав, що поділюсь, як це зробив у підсумку, я просто додав у своїй діяльності:

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

І це працює саме так, як я хочу. Включаючи скидання стану під час відновлення діяльності.


15
Завдяки цьому рішенню обидві тиснені преси можуть мати між собою довільну кількість часу. Тож ви можете натиснути Backодин раз, а потім Backще раз натиснути через хвилину, і програма вийде. Користувач не очікує такої поведінки.
BruceHill

1
Я думаю, що це питання смаку. У цьому випадку ви повідомляєте користувача лише один раз, тож користувач знає, що він перебуває в основній діяльності, і інший натисненням назад вийде з програми (можливо, після того, як користувач кілька разів натисне назад, щоб повернутися на головний екран). Якщо пізніше користувач знову натисне назад, ми можемо припустити, що він хоче вийти з програми (якщо тільки він не перейшов до іншої діяльності і, мабуть, втратив слід від того, наскільки глибоко він пройшов). У прийнятій відповіді вище, ви вважаєте, що користувач може забути, що він уже знаходиться на головному екрані. І те й інше добре, залежно від того, що ви хочете чи врахуєте.
Ферран Мейлінч

4
@FerranMaylinch - я не згоден. Це не лише питання смаку. Якщо минуло значне час, слід припустити, що користувач тим часом робив інші дії, і більше не розглядає те, що робив раніше, і вирішив не продовжувати подавати заявку. Дійсно, всі, крім самого виняткового користувача, навіть не матимуть його на увазі, що раніше він це робив. Без обмеження часу ви залишили додаток у невидимому режимі , про який користувач не може знати. Я абсолютно вважаю це поганим дизайном інтерфейсу користувача. Користувачі будуть здивовані.
ToolmakerSteve

ІМХО, кращі варіанти щодо цього тут і тут .
ToolmakerSteve

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

1
Я споконвічно проголосував це за корисне. Проблема, чомусь, на деяких телефонах це не працює. Метод onBackPress працює найкраще, але тоді у вас немає часових позначок, тому вам потрібні обробники, як зазначено у прийнятій відповіді.
ravemir

23

Серед цих відповідей є найпростіший спосіб.

Просто напишіть наступний код всередині 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();
}

Потрібно визначити back_pressedоб’єкт як longу діяльності.


16

Моє рішення за допомогою закусочної:

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

Простий і стильний.


2
Дякую за це рішення. Простий, ефективний, без ризику.
ping li

2
Рішення поза коробкою. (
хлопати

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

}

Я сподіваюся, що це буде корисно !!


Ви впевнені , видалення обробника в onBackPressed()Закріпити витоку пам'яті проблеми?
Saro Taşciyan

@ Zefnus: Це виправить, наскільки я знаю. Виправте мене, якщо я помиляюся. Як ви простежили проблему з пам'яттю з вищевказаним кодом?
Мехул Джойсар

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

Оголосити змінноюprivate boolean doubleBackToExitPressedOnce = false;

Вставте це у свою Основну діяльність, і це вирішить вашу проблему


10

Під час виходу з програми не дуже гарна ідея використовувати Runnable, я нещодавно з'ясував набагато простіший спосіб запису та порівняння періоду між двома натисканнями кнопки НАЗАД. Приклад коду наступним чином:

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 мілісекунд у вибірці.


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

7

Це не вбудована функціональність. Я думаю, це навіть не рекомендована поведінка. Програми для Android не призначені для виходу:

Чому програми Android не надають опцію "Вихід"?


Точка взята. Під виходом я мав на увазі "повернення до головного екрану"
Гійом

3
Все-таки це не вбудований функціонал. Однак я не знаю жодних вказівок проти цього. Як користувач Android, мені подобається така функціональність.
Caner

6
  1. Оголосити глобальну змінну Toast для класу MainActivity. приклад: Toast exitToast;
  2. Ініціалізуйте його у методі перегляду onCreate. приклад: exitToast = Toast.makeText (getApplicationContext (), "Знову натисніть, щоб вийти", Toast.LENGTH_SHORT);
  3. Нарешті створіть onBackPressMethod як наступним чином:

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

Це працює правильно, я перевірив. і я думаю, що це набагато простіше.


5

Zefnus «s відповідь з допомогою 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();
   }
}

1
Це хороший спосіб зробити це, але якщо у вас є більше тостів, це не так.
Болдіяр Пол

@BoldijarPaul Ні, цей код перевіряє лише статус конкретного тосту. Тож будь-які інші тости не впливатимуть на поведінку.
терміново

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

Код вище передбачає, що використовується бібліотека підтримки. Якщо ви використовуєте фрагменти, але не бібліотеку підтримки, ви хочете замінити 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);
}

5

В яві

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

    }
}

В котлін

 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)

        }
    }

4

Я знаю, що це дуже старе питання, але це найпростіший спосіб робити те, що ти хочеш.

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

Я знаю, що це не найкращий метод, але він працює чудово!


3
Це не загальна поведінка "двічі натиснути кнопку назад для виходу". Як і коментар BruceHill щодо прийнятої відповіді вказує, ваша відповідь теж не обробляє питання часу
inankupeli

але при цьому ви можете натиснути назад, воно покаже повідомлення, а потім ви можете зачекати ще трохи, поверніться ще раз, і він закриє додаток, і він не буде обробляти поведінку подвійного зворотного часу
Gastón Saillén

3

Для цього я реалізував таку функцію:

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

3

Це також допомагає, коли попередня робота стека зберігається в стеці.

Я змінив відповідь 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);
} 

2
@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
Як це справляється навіть із сценарієм подвійної преси? Як тільки я повернувся назад, це запускає активність.
kilokahn

2

Трохи кращий метод, ніж я вважаю Зефнусом . Викличте 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();
    }
}

2

Ось повний робочий код. А також не забудьте видалити зворотні дзвінки, щоб це не спричинило витік пам'яті в додатку. :)

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

2

Тут я узагальнив написання коду для 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();
  }

2

Я цим користуюся

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

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

або ExitApp.now(this,R.string.double_back_pressed)

для зміни секунд потрібно близько, заданих мілісекунд

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


2

Коли HomeActivity містить ящик для навігації та подвійну функцію backPress () для виходу з програми. (Не забудьте ініціювати глобальну змінну boolean doubleBackToExitPressOnce = false;) новий обробник через 2 секунди встановити змінну doubleBackPressOnce на 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);
        }
    }
}

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

1

Деякі вдосконалення відповіді 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);
        }

1

Це те саме, що було прийнято і найбільш проголосовано, але цей фрагмент використав Snackbar замість 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);
    }

1

У моєму випадку я залежу від 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();
    }
}

1

Для діяльності, у якій є Навігаційний ящик, використовуйте наступний код для OnBackPress ()

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

1

Ось ще один спосіб ... за допомогою методу 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();
        }

    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.