Дважды нажмите кнопку «Назад», чтобы выйти из игры.

331

В последнее время я заметил этот паттерн во многих приложениях и играх для Android: при нажатии кнопки «Назад» для «выхода» из приложения Toastпоявляется сообщение, похожее на «Пожалуйста, нажмите НАЗАД еще раз, чтобы выйти».

Мне было интересно, поскольку я вижу это все чаще и чаще, это встроенная функция, к которой вы можете как-то получить доступ в действии? Я посмотрел на исходный код многих классов, но, похоже, ничего не могу найти по этому поводу.

Конечно, я могу придумать несколько способов достичь той же функциональности довольно легко (возможно, самый простой - сохранить логическое значение в действии, которое указывает, нажал ли пользователь уже один раз ...), но мне было интересно, есть ли здесь что-то уже ,

РЕДАКТИРОВАТЬ : Как упоминалось @LAS_VEGAS, я не имел в виду «выход» в традиционном смысле. (то есть прекращено) Я имел в виду «вернуться к тому, что было открыто до запуска активности запуска приложения», если это имеет смысл :)

Гийом
источник
[Android - Подтвердите выход приложения с помощью тоста] [1]: stackoverflow.com/questions/14006461/…
resource8218
1
У меня была такая же проблема при использовании библиотеки HoloEverywhere, слишком просто вы можете добавить android: launchMode = "singleTask" к вашему определению активности в файле манифеста.
Сохайб Хассун
Другое решение stackoverflow.com/questions/8430805/…
Webserveis

Ответы:

949

В Java Activity:

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

Судхиш Б Наир
источник
44
Лучший ответ! Вы также можете добавить условие if (doubleBackToExitPressedOnce || fragManager.getBackStackEntryCount ()! = 0) {в случае добавления на основе фрагментов
Антон Деревянко
2
Я согласен, это, безусловно, лучший ответ и должен быть принятым ответом.
BruceHill
3
Вы должны удалить Runnable при выходе из приложения.
Уэйн
Проверьте мой ответ. Я изменил ответ, данный, Sudheesh B Nairчтобы покрыть вышеупомянутые предложенные комментарии.
Мехул Джойсар
19
Это хорошее быстрое решение / ответ, но я не согласен, что это лучшее решение . И для тех, кто думает, что это будет лучшим ответом снова, я не могу согласиться. Это решение вызывает утечки и потребует дополнительных усилий для обработки. Проверьте ответы ниже для получения дополнительной информации.
Саро Ташчиян
226

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

Назад на принятый ответ критика ; Использование flagдля указания , если она была нажата в прошлом TIME_INTERVAL(скажем , 2000) миллисекунды и набор - сброс осуществляется через Handler«S postDelayed()метод был первым , что приходит на мой взгляд. Но 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);
}

Спасибо @NSouth за помощь; Чтобы предотвратить появление всплывающего сообщения даже после закрытия приложения, оно Toastможет быть объявлено как член, скажем, mExitToastи может быть отменено mExitToast.cancel();непосредственно перед super.onBackPressed();вызовом.

Саро Ташчиян
источник
9
Для тех, кто думает, что это то же самое, что сказал Судхи Б Наир: та же функциональность, лучшая производительность. так +1.
BedirYilmaz
4
Мне нравится этот ответ, и я думаю, что он самый лучший. Я имею в виду, я не думаю, что это, это лучший ответ, по причинам, изложенным выше. Я надеюсь, что вы получите больше голосов за этот. Один комментарий: никто не считает странным, что тост сохраняется через пару секунд после закрытия приложения? Никто не хочет отменить тост? Я знаю, что это может быть маленькая деталь, но я думаю, что это должно произойти. Что, вы парни, думаете?
acrespo
1
@joonty спасибо за редактирование, это ключевое слово int отсутствует в течение некоторого времени. Он скомпилируется сейчас (:
Саро Ташчиян
2
@NSouth Второй блок кода представлял собой пример с использованием mHandlers, чтобы показать, что он требует больше усилий. Я предлагаю вам рассмотреть возможность использования первого блока кода, который не использует обработчик.
Саро Ташчиян
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;
}
vHow
источник
1
Я изначально проголосовал за это как за помощь. Проблема в том, что по некоторым причинам это не работает на некоторых телефонах. Метод onBackPressed работает лучше всего, но тогда у вас нет временных меток, поэтому вам нужны обработчики в соответствии с принятым ответом.
Равемир
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
Спасибо за это решение. Просто, эффективно, без риска.
Пинг Ли
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()устранить проблему утечки памяти ?
Саро Ташсиян
@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;

Вставьте это в ваш основной вид деятельности, и это решит вашу проблему

Rakshith
источник
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();
}

Это сделает выход из приложения двойным нажатием кнопки BACK в течение определенного периода задержки, который составляет 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. Наконец, создайте метод onBackPressedMethod следующим образом:

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

Это работает правильно, я проверил. и я думаю, что это намного проще.

Supto
источник
5

Ответ Зефнуса с использованием 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)

        }
    }
Praveen
источник
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
Это не общее поведение «дважды нажать кнопку« Назад »для выхода». Как отмечается в комментарии БрюсХилла о принятом ответе, ваш ответ также не
решает
но с этим вы можете щелкнуть назад, он покажет сообщение, а затем вы можете немного подождать, вернуться снова и закроет приложение, и он не будет обрабатывать поведение двойной
задержки
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

Это также помогает, когда в стеке хранится предыдущая активность стека.

Я изменил ответ Судхиша

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
Как это справляется со сценарием двойного нажатия? Как только я нанес ответный удар, начинается действие.
килокан
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();
    }
}
k8C
источник
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 () для опции backpress или logout.

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)

Webserveis
источник
2

Когда HomeActivity содержит блок навигации и двойную функцию backPressed () для выхода из приложения. (Не забудьте инициализировать глобальную переменную boolean doubleBackToExitPressedOnce = false;) Новый обработчик через 2 секунды устанавливает переменную doubleBackPressedOnce в значение 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);
}
maniix
источник
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

Это тот же самый принятый и получивший наибольшее количество голосов ответ, но вместо этого использовался снэк-бар вместо тоста.

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);
    }
MobileOS
источник
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

Для действия, в котором есть Навигационный ящик , используйте следующий код для 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();
            }
        }
    }
Прия Раджан
источник
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();
        }

    }
Ануй Саин
источник