Dwukrotne kliknięcie przycisku Wstecz, aby zakończyć działanie


330

Ostatnio zauważyłem ten wzór w wielu aplikacjach i grach na Androida: po kliknięciu przycisku Wstecz, aby „wyjść” z aplikacji, Toastpojawia się komunikat podobny do tego: „Kliknij ponownie BACK, aby wyjść”.

Zastanawiam się, ponieważ coraz częściej to widzę, czy jest to wbudowana funkcja, do której można uzyskać dostęp w ramach działania? Patrzyłem na kod źródłowy wielu klas, ale nie mogę nic na ten temat znaleźć.

Oczywiście mogę dość łatwo wymyślić kilka sposobów na uzyskanie tej samej funkcjonalności (najłatwiej jest zachować wartość logiczną w działaniu, która wskazuje, czy użytkownik kliknął już raz ...), ale zastanawiałem się, czy coś już tu jest .

EDYCJA : Jak wspomniałem @LAS_VEGAS, tak naprawdę nie miałem na myśli „wyjścia” w tradycyjnym znaczeniu. (tj. zakończone) Miałem na myśli „powrót do tego, co było otwarte przed uruchomieniem działania aplikacji”, jeśli ma to sens :)


[Android - Potwierdź zamknięcie aplikacji za pomocą toastu] [1]: stackoverflow.com/questions/14006461/...
resource8218

1
Miałem ten sam problem podczas korzystania z biblioteki HoloEverywhere, zbyt łatwo możesz dodać Androida: launchMode = "singleTask" do definicji aktywności w pliku manifestu.
Sohayb Hassoun


Odpowiedzi:


947

W aktywności 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);
} 

W działalności Kotlin:

private var doubleBackToExitPressedOnce = false
override fun onBackPressed() {
        if (doubleBackToExitPressedOnce) {
            super.onBackPressed()
            return
        }

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

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

Myślę, że ten moduł obsługi pomaga zresetować zmienną po 2 sekundach.


43
Najlepsza odpowiedź! Możesz również dodać warunek, jeśli (doubleBackToExitPressedOnce || fragmentManager.getBackStackEntryCount ()! = 0) {w przypadku dodania opartego na fragmentach
Anton Derevyanko,

2
Zgadzam się, to zdecydowanie najlepsza odpowiedź i powinna być odpowiedzią zaakceptowaną.
BruceHill

3
Powinieneś usunąć Runnable po wyjściu z aplikacji.
Wayne

Sprawdź moją odpowiedź. Zmodyfikowałem odpowiedź udzieloną przez, Sudheesh B Nairaby uwzględnić wyżej sugerowane komentarze.
Mehul Joisar

18
To ładne szybkie rozwiązanie / odpowiedź, ale nie zgadzam się, że to najlepsze rozwiązanie . A dla tych, którzy myślą, że to będzie najlepsza odpowiedź, nie mogę się zgodzić. Takie rozwiązanie powoduje wycieki i będzie wymagało dodatkowego wysiłku przy manipulowaniu. Sprawdź aswers poniżej dla dalszych szczegółów.
Saro Taşciyan

226

Sudheesh B Nair ma miłą (i zaakceptowaną) odpowiedź na pytanie, które moim zdaniem powinno mieć lepszą alternatywę, taką jak;

Co jest złego w mierzeniu czasu i sprawdzaniu, czy minęły TIME_INTERVALmilisekundy (powiedzmy 2000) od ostatniego naciśnięcia wstecz. Wywoływany jest następujący przykładowy kod System.currentTimeMillis();do przechowywania czasu 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();
}

Powrót do przyjętej krytyki odpowiedzi ; Używanie flagdo wskazania, czy został wciśnięty w ostatnim TIME_INTERVAL(powiedzmy 2000) milisekund i set - Reset jest poprzez Handler„s postDelayed()metoda była pierwszą rzeczą, aby przyjść w moim umyśle. Ale postDelayed()akcja powinna zostać anulowana, gdy aktywność się zamyka, usuwając Runnable.

Aby go usunąć Runnable, nie można go ogłosić anonimowym i należy go również zgłosić jako członka wraz z Handlerzapisem. Następnie można odpowiednio wywołać removeCallbacks()metodęHandler

Poniższa próbka to demonstracja;

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

Dzięki @NSouth za wkład; Aby zapobiec pojawianiu się wiadomości toast nawet po zamknięciu aplikacji, Toastmożna ją zadeklarować jako członka - powiedzmy mExitToast- i można ją anulować mExitToast.cancel();bezpośrednio przed super.onBackPressed();połączeniem.


9
Dla tych, którzy myślą, że to samo, co powiedział Sudheesh B Nair: Ta sama funkcjonalność, lepsza wydajność. więc +1.
BedirYilmaz

4
Podoba mi się ta odpowiedź i myślę, że jest najlepsza. Mam na myśli, że NIE MYŚLĘ, że jest to najlepsza odpowiedź z wyżej wymienionych powodów. Mam nadzieję, że otrzymacie więcej pozytywnych opinii na ten temat. Jeden komentarz: nikt nie uważa za dziwne, że toast utrzymuje się kilka sekund po zamknięciu aplikacji? Nikt nie chce anulować toastu? Wiem, że może to być mały szczegół, ale myślę, że tak powinno się stać. Co myślicie?
zgodnie z

1
@joonty dzięki za edycję, tego słowa kluczowego int brakuje od jakiegoś czasu. Skompiluje się teraz (:
Saro Taşciyan

2
@NSouth Drugi blok kodu był próbką przy użyciu mHandlerów, aby pokazać, że wymaga więcej wysiłku. Sugeruję rozważenie zastosowania pierwszego bloku kodu, który nie używa modułu obsługi.
Saro Taşciyan

1
@acrespo Przypuszczam, że mamy solidne rozwiązanie dla wiadomości tostowej, która utrzymuje się po zamknięciu aplikacji .
Saro Taşciyan

30

Pomyślałem, że w końcu podzielę się tym, jak to zrobiłem, dodałem w swojej działalności:

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

I to działa dokładnie tak, jak chcę. Łącznie z resetowaniem stanu przy każdym wznowieniu działania.


15
Dzięki temu rozwiązaniu dwie prasy tylne mogą mieć dowolną ilość czasu między nimi. Możesz więc nacisnąć Backraz, a następnie Backjeszcze raz minutę później, a aplikacja zostanie zamknięta. Tego nie będzie oczekiwał użytkownik.
BruceHill

1
Myślę, że to kwestia gustu. W takim przypadku powiadomisz użytkownika tylko raz, aby wiedział, że jest w głównej aktywności, a kolejne naciśnięcie wstecz spowoduje zamknięcie aplikacji (prawdopodobnie po kilkukrotnym naciśnięciu tego przycisku, aby wrócić do ekranu głównego). Jeśli później użytkownik ponownie się wycofa, możemy przypuszczać, że chce wyjść z aplikacji (chyba że przejdzie do innych działań i prawdopodobnie stracił kontrolę nad głębokością). W zaakceptowanej odpowiedzi powyżej uważasz, że użytkownik może zapomnieć, że jest już na ekranie głównym. Oba są w porządku, w zależności od tego, co chcesz lub wziąć pod uwagę.
Ferran Maylinch

4
@FerranMaylinch - Nie zgadzam się. To nie jest tylko kwestia gustu. Jeśli minęło dużo czasu, powinniśmy założyć, że użytkownik w międzyczasie wykonał inne czynności i nie bierze już pod uwagę tego, co zrobił wcześniej, i postanowił nie kontynuować . Rzeczywiście, wszyscy oprócz najbardziej wyjątkowego użytkownika nie będą nawet mieli w głowie, że wcześniej to robił. Bez limitu czasu aplikacja została pozostawiona w trybie niewidocznym , o którym użytkownik nie ma pojęcia. Absolutnie uważam to za kiepski projekt interfejsu użytkownika. Użytkownicy będą zaskoczeni.
ToolmakerSteve

IMHO, lepsze warianty tutaj i tutaj .
ToolmakerSteve

26

Schemat przebiegu procesu: Naciśnij ponownie, aby wyjść.

Kod 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
Początkowo głosowałem za pomocą. Problem w tym, że z jakiegoś powodu to nie działa na niektórych telefonach. Metoda onBackPressed działa najlepiej, ale wtedy nie masz znaczników czasu, więc potrzebujesz procedur obsługi, ponieważ stany są akceptowane.
ravemir

23

Wśród tych wszystkich odpowiedzi jest bardzo najprostszy sposób.

Po prostu napisz następujący kod w onBackPressed()metodzie.

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

Musisz zdefiniować back_pressedobiekt jak longw działaniu.


16

Moje rozwiązanie z użyciem paska przekąskowego:

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

Prosty i stylowy.


2
Dzięki za to rozwiązanie. Prosty, skuteczny, bez ryzyka.
ping li

2
Rozwiązanie gotowe. (klaskać)
Hitesh Dhamshaniya

13

W oparciu o poprawną odpowiedź i sugestie w komentarzach, stworzyłem demo, które działa absolutnie dobrze i usuwa wywołania programu obsługi po użyciu.

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

}

Mam nadzieję, że będzie to pomocne !!


Czy na pewno usuwasz program obsługi, aby onBackPressed()naprawić problem wycieku pamięci ?
Saro Taşciyan

@Zefnus: Naprawi to, o ile mi wiadomo. Popraw mnie, jeśli się mylę. Jak prześledziłeś problem pamięci z powyższym kodem?
Mehul Joisar

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

Deklaruj zmiennąprivate boolean doubleBackToExitPressedOnce = false;

Wklej to w swojej głównej działalności, a to rozwiąże problem


10

Nie jest dobrym pomysłem używanie Runnable po wyjściu z aplikacji, ostatnio wymyśliłem znacznie prostszy sposób rejestrowania i porównywania okresu między dwoma kliknięciami przycisku BACK. Przykładowy kod w następujący sposób:

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

To sprawi, że wyjdziesz z aplikacji podwójnym kliknięciem przycisku BACK w pewnym okresie opóźnienia, który wynosi 2000 milisekund w próbce.


8

Zaakceptowana odpowiedź jest najlepsza, ale jeśli używasz Android Design Support Library, możesz użyć SnackBardla lepszych widoków.

   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

To nie jest wbudowana funkcjonalność. Myślę, że to nawet nie jest zalecane zachowanie. Aplikacje na Androida nie są przeznaczone do wyjścia:

Dlaczego aplikacje na Androida nie oferują opcji „Wyjdź”?


Punkt wzięty. Przez wyjście miałem na myśli „powrót do ekranu głównego”
Guillaume,

3
Nadal nie jest to wbudowana funkcjonalność. Nie znam jednak żadnych wytycznych w tej sprawie. Jako użytkownik Androida podoba mi się taka funkcjonalność.
Caner,

6
  1. Zadeklaruj globalną zmienną Toast dla klasy MainActivity. przykład: Toast exitToast;
  2. Zainicjuj go w metodzie widoku onCreate. przykład: exitToast = Toast.makeText (getApplicationContext (), „Naciśnij ponownie, aby wyjść”, Toast.LENGTH_SHORT);
  3. Na koniec utwórz onBackPressedMethod w następujący sposób:

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

Działa to poprawnie, przetestowałem. i myślę, że jest to o wiele prostsze.


5

Odpowiedź Zefnusa przy użyciu System.currentTimeMillis () jest najlepsza (+1). Sposób, w jaki to zrobił to nie lepsze niż to, ale nadal opublikowania go dodać do powyższych pomysłów.

Jeśli toast nie jest widoczny po naciśnięciu przycisku Wstecz, toast jest wyświetlany, a jeśli jest widoczny (tył został już naciśnięty jeden raz w ostatnim Toast.LENGTH_SHORTczasie), to wychodzi.

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
To dobry sposób, aby to zrobić, ale jeśli masz więcej wiadomości tostowych, nie jest.
Boldijar Paul,

@BoldijarPaul Nie, ten kod sprawdza tylko status tego konkretnego tostu. Więc wszelkie inne tosty nie wpłyną na zachowanie.
urgentx

5

Ostatnio musiałem zaimplementować tę funkcję przycisku Wstecz w mojej aplikacji. Odpowiedzi na pierwotne pytanie były przydatne, ale musiałem wziąć pod uwagę jeszcze dwa punkty:

  1. W niektórych momentach przycisk Wstecz jest wyłączony
  2. Główną czynnością jest używanie fragmentów w połączeniu z tylnym stosem

Na podstawie odpowiedzi i komentarzy stworzyłem następujący kod:

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

Powyższy kod zakłada, że ​​używana jest biblioteka obsługi. Jeśli używasz fragmenty, ale nie z biblioteki wsparcia, chcesz zastąpić getSupportFragmentManager()przez getFragmentManager().

Usuń pierwszy if, jeśli przycisk Wstecz nigdy nie jest anulowany. Usuń drugi if, jeśli nie używasz fragmentów lub stosu fragmentów

Ważne jest również, aby pamiętać, że metoda onBackPressedjest obsługiwana od Androida 2.0. Sprawdź tę stronę, aby uzyskać szczegółowy opis. Aby funkcja back press działała również w starszych wersjach, dodaj następującą metodę do swojej aktywności:

@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

W java

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

    }
}

W kotlinie

 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

Wiem, że to bardzo stare pytanie, ale jest to najłatwiejszy sposób na robienie tego, co chcesz.

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

Wiem, że to nie jest najlepsza metoda, ale działa dobrze!


3
To nie jest ogólne zachowanie „dwukrotnego kliknięcia przycisku Wstecz, aby wyjść”. Jak wskazuje komentarz BruceHilla na temat zaakceptowanej odpowiedzi, twoja odpowiedź nie dotyczy również problemu z czasem
inankupeli

ale dzięki temu możesz kliknąć wstecz, wyświetli się komunikat, a następnie możesz poczekać trochę dłużej, wrócić ponownie i zamknie aplikację, i nie będzie obsługiwać zachowania podwójnego cofania
Gastón Saillén

3

W tym celu wdrożyłem następującą funkcję:

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

Pomaga to również wtedy, gdy poprzednia aktywność stosu jest zapisana w stosie.

Zmodyfikowałem odpowiedź 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
Jak to w ogóle obsługuje scenariusz podwójnego naciśnięcia? Jak tylko wrócę, uruchamia to działanie.
kilokahn

2

Myślę, że jest to nieco lepsza metoda niż Zefnus . Wywołaj System.currentTimeMillis () tylko jeden raz i pomiń 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

Oto pełny działający kod. Nie zapomnij również usunąć wywołań zwrotnych, aby nie spowodowało to wycieku pamięci w aplikacji. :)

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

Tutaj uogólniłem napisanie kodu dla liczby zliczeń N. Kod jest podobnie napisany dla opcji Enable Developer w telefonie z systemem Android. Nawet ty możesz tego użyć, aby włączyć funkcje podczas testowania aplikacji przez programistę.

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

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

 }

Wywołaj askToExit () w opcji backpress lub wylogowania.

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

używam tego

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

}

użyj w przypadkuonBackPressed

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

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

do zmiany sekund potrzeba zamknięcia, określonych milisekund

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


2

Gdy HomeActivity zawiera szufladę nawigacji i funkcję backPressed (), aby wyjść z aplikacji. (Nie zapomnij zainicjować zmiennej globalnej boolean doubleBackToExitPressedOnce = false;) nowy moduł obsługi po 2 sekundach ustaw zmienną doubleBackPressedOnce na 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

Kilka ulepszeń w odpowiedzi Sudheesh B Naira, zauważyłem, że będzie czekał na moduł obsługi, nawet natychmiast naciskając dwukrotnie, więc anuluj moduł obsługi, jak pokazano poniżej. Anulowałem także toast, aby nie wyświetlał się po zamknięciu aplikacji.

 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

To jest tak samo jak zaakceptowana i najczęściej głosowana odpowiedź, ale ten wycięty użył Snackbar zamiast 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

W moim przypadku polegam na Snackbar#isShown()lepsze 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

W przypadku działania, które ma szufladę nawigacji , użyj następującego kodu dla 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

Oto inny sposób ... przy użyciu metody 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();
        }

    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.