Jak wyświetlić okno dialogowe potwierdzające, że użytkownik chce wyjść z działania na Androida?

274

Próbowałem pokazać „Czy chcesz wyjść?” rodzaj okna dialogowego, gdy użytkownik próbuje wyjść z działania.

Nie mogę jednak znaleźć odpowiednich haków interfejsu API. Activity.onUserLeaveHint()początkowo wyglądało obiecująco, ale nie mogę znaleźć sposobu, aby zatrzymać działanie przed zakończeniem.

Peter A.
źródło
39
Czy jest to absolutnie niezbędne, aby mieć to pytanie? Gdy użytkownik chce zakończyć działanie, powinien móc to zrobić natychmiast. Możesz przemyśleć swoją strategię.
Tom R
4
Wyświetlanie opcji potwierdzenia wyjścia jest obowiązkowe w przypadku aukcji w sklepie App Store firmy Samsung. Jedna z moich aplikacji została odrzucona za brak tego.
John Ashmore,
6
To nieznośne, @Samsung.
dokkaebi
3
To zależy od tego, co chcesz zrobić po wyjściu. Jeśli stan zostanie zachowany i możesz po prostu cofnąć się, pokazanie okna dialogowego może nie być konieczne. Jednak w jednej z moich aplikacji na stałe usuwam pliki cookie, dane i zamykam połączenia z ostatecznym zatwierdzeniem danych. Użytkownik powinien zostać poinformowany o ostateczności swojego wyboru, tym bardziej, że dzisiaj takie poczucie ostateczności w aplikacji mobilnej jest rzadkością.
Eric,
15
@ Tom R: Całkowicie się nie zgadzam. Istnieją aplikacje, których nie chcesz zakończyć przypadkowo. Pracują w środowisku biznesowym i tak dalej.
TomeeNS

Odpowiedzi:

390

W systemie Android 2.0+ wyglądałoby to tak:

@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle("Closing Activity")
        .setMessage("Are you sure you want to close this activity?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();    
        }

    })
    .setNegativeButton("No", null)
    .show();
}

We wcześniejszych wersjach wyglądałoby to tak:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    //Handle the back button
    if(keyCode == KeyEvent.KEYCODE_BACK) {
        //Ask the user if they want to quit
        new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.quit)
        .setMessage(R.string.really_quit)
        .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

                //Stop the activity
                YourClass.this.finish();    
            }

        })
        .setNegativeButton(R.string.no, null)
        .show();

        return true;
    }
    else {
        return super.onKeyDown(keyCode, event);
    }

}
jax
źródło
15
Również w wersji 2.0 i nowszej pojawiło się nowe wydarzenie onBackPressed, które jest zalecane w stosunku do onKeyDown developer.android.com/intl/zh-TW/reference/android/app/... Tutaj znajduje się sekcja omawiająca zmiany i nowe zalecane podejście. developer.android.com/intl/zh-TW/sdk/android-2.0.html
Patrick Kafka
3
Wpis na blogu na temat złapania klawisza Wstecz tutaj: android-developers.blogspot.com/2009/12/… Pamiętaj, że nie pozwala to na złapanie innych sposobów, w jakie użytkownik może opuścić aplikację: naciśnięcie domu, wybranie powiadomienia, otrzymanie telefonu zadzwoń itp.
hackbod
Działa to doskonale, ale jak wymusić zatrzymanie wykonywania kodu podczas wyświetlania go użytkownikowi?
anon58192932
znalazłem, to świetne rozwiązanie tutaj: stackoverflow.com/questions/4381296/…
anon58192932
1
To jest metoda (), której szukam, ale nie wiedziałem, gdzie wywołać tę metodę ().
user2841300,
186
@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   ExampleActivity.super.onBackPressed();
               }
           })
           .setNegativeButton("No", null)
           .show();
}
Chanakya Vadla
źródło
5
ElBradford skomentował: Wywołanie super onBackPressed jest lepsze niż założenie, że onBackPressed wywoła tylko wykończenie (). Nawet jeśli jest to teraz prawdą, może nie być prawdą w przyszłych interfejsach APICustomTabActivity.super.onBackPressed
mplungjan,
@mplungjan Czy możesz wyjaśnić swój komentarz ... Chcesz powiedzieć, że lepiej zastąpić finish()kod super.onBackPressed()?
zakaz geoinżynierii
Komentarz ma 3 lata. Nie mam pojęcia, co jest dobrą praktyką w 2015 r.
mplungjan,
@mplungjan Twój komentarz dotyczył przyszłych interfejsów API, ale i tak byłoby pomocne, gdybyś mógł to wyjaśnić.
zakaz geoinżynierii
Nie ma problemu. Właśnie próbowałem MyActivity.super.onBackPressed();i działa dobrze dla mnie. Wydaje się to również najbardziej logiczne - wywołać metodę, którą zastępujesz, jeśli chcesz standardowego zachowania, gdy użytkownik stuknie „Nie”.
zakaz geoinżynierii
33

Zmodyfikowałem kod @ user919216 .. i dostosowałem go do WebView

@Override
public void onBackPressed() {
    if (webview.canGoBack()) {
        webview.goBack();

    }
    else
    {
     AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                finish();
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
AlertDialog alert = builder.create();
alert.show();
    }

}
suraj jain
źródło
22

Wolę wyjść z dwukrotnym dotknięciem przycisku Wstecz niż z oknem wyjścia.

W tym rozwiązaniu pokazuje toast, gdy wracasz po raz pierwszy, ostrzegając, że kolejne naciśnięcie wstecz spowoduje zamknięcie aplikacji. W tym przykładzie mniej niż 4 sekundy.

private Toast toast;
private long lastBackPressTime = 0;

@Override
public void onBackPressed() {
  if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
    toast = Toast.makeText(this, "Press back again to close this app", 4000);
    toast.show();
    this.lastBackPressTime = System.currentTimeMillis();
  } else {
    if (toast != null) {
    toast.cancel();
  }
  super.onBackPressed();
 }
}

Token od: http://www.androiduipatterns.com/2011/03/back-button-behavior.html

Toni Gamez
źródło
Ten jest zdecydowanie moim ulubionym, ponieważ Dialogbox może być nieporęczny. Lekko go zmieniłem, aby uzyskać 3 sekundy i wydaje się nieco bardziej naturalny z tym znacznikiem. Dzięki za post.
PGMacDesign
najlepiej przy podwójnym naciśnięciu wstecz aplikacja powinna wyjść, ale w moim kodzie jest otwarta Aktywność logowania (oznacza aktywność wstecz z włączonym
pokrętłem
21

Jeśli nie masz pewności, czy wywołanie „wstecz” spowoduje zamknięcie aplikacji lub przeniesie użytkownika do innej czynności, możesz zawrzeć powyższe odpowiedzi w czeku, isTaskRoot (). Może się to zdarzyć, jeśli główna aktywność może być wielokrotnie dodana do tylnego stosu lub jeśli manipulujesz historią stosu wstecznego.

if(isTaskRoot()) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                YourActivity.super.onBackPressed;
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
    AlertDialog alert = builder.create();
    alert.show();

} else {
    super.onBackPressed();
}
Radość
źródło
5

Korzystanie z Lambda:

    new AlertDialog.Builder(this).setMessage(getString(R.string.exit_msg))
        .setTitle(getString(R.string.info))
        .setPositiveButton(getString(R.string.yes), (arg0, arg1) -> {
            moveTaskToBack(true);
            finish();
        })
        .setNegativeButton(getString(R.string.no), (arg0, arg1) -> {
        })
        .show();

Musisz także ustawić poziom języka, aby obsługiwał java 8 w swoim gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}
wasiljevski
źródło
5

w Chinach większość aplikacji potwierdzi zakończenie przez „dwukrotne kliknięcie”:

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);
} 
Siwei Shen 申思维
źródło
5

Najpierw usuń super.onBackPressed();z onbackPressed()metody niż i poniżej kodu:

@Override
public void onBackPressed() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    MyActivity.this.finish();
               }
           })
           .setNegativeButton("No", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
               }
           });
    AlertDialog alert = builder.create();
    alert.show();

}
Anjali-Systematix
źródło
2

Lubię podejście @GLee i używanie go z fragmentem jak poniżej.

@Override
public void onBackPressed() {
    if(isTaskRoot()) {
        new ExitDialogFragment().show(getSupportFragmentManager(), null);
    } else {
        super.onBackPressed();
    }
}

Dialog przy użyciu fragmentu:

public class ExitDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.exit_question)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getActivity().finish();
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getDialog().cancel();
                }
            })
            .create();
    }
}
marioosh
źródło
2
Just put this code in your first activity 

@Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
            drawerLayout.closeDrawer(GravityCompat.END);
        }
        else {
// if your using fragment then you can do this way
            int fragments = getSupportFragmentManager().getBackStackEntryCount();
            if (fragments == 1) {
new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    finish();
               }
           })
           .setNegativeButton("No", null)
           .show();


            } else {
                if (getFragmentManager().getBackStackEntryCount() > 1) {
                    getFragmentManager().popBackStack();
                } else {

           super.onBackPressed();
                }
            }
        }
    }
Mohit Hooda
źródło
y pierwsza aktywność, nie mogę umieścić w aktywności na środku. ponieważ teraz jsut zamyka bieżącą aktywność i pokazuje poprzednią
shareef
1

Inną alternatywą byłoby pokazanie Toast/ Snackbarprzy pierwszym naciśnięciu wstecz, prosząc o ponowne naciśnięcie, aby wyjść , co jest o wiele mniej uciążliwe niż pokazanie, AlertDialogczy użytkownik chce wyjść z aplikacji.

Możesz to zrobić DoubleBackPress Android Libraryza pomocą kilku linii kodu. Przykład GIF pokazujący podobne zachowanie.

Na początek dodaj zależność do aplikacji:

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

Następnie w swoim działaniu zaimplementuj wymagane zachowanie.

// set the Toast to be shown on FirstBackPress (ToastDisplay - builtin template)
// can be replaced by custom action (new FirstBackPressAction{...})
FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);

// set the Action on DoubleBackPress
DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
    @Override
    public void actionCall() {
        // TODO : Exit the application
        finish();
        System.exit(0);
    }
};

// setup DoubleBackPress behaviour : close the current Activity
DoubleBackPress doubleBackPress = new DoubleBackPress()
        .withDoublePressDuration(3000)     // msec - wait for second back press
        .withFirstBackPressAction(firstBackPressAction)
        .withDoubleBackPressAction(doubleBackPressAction);

Na koniec ustaw to jako zachowanie na prasie tylnej.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
Kaushik NP
źródło