Come mostrare una finestra di dialogo per confermare che l'utente desidera uscire da un'attività Android?


274

Ho provato a mostrare un "Vuoi uscire?" tipo di finestra di dialogo quando l'utente tenta di uscire da un'attività.

Tuttavia, non riesco a trovare gli hook API appropriati. Activity.onUserLeaveHint()inizialmente sembrava promettente, ma non riesco a trovare un modo per fermare il completamento dell'attività.


39
È assolutamente essenziale che tu abbia questo prompt? Quando un utente desidera terminare un'attività, dovrebbe essere in grado di farlo immediatamente. Potresti voler ripensare la tua strategia.
Tom R,

4
La visualizzazione di un'opzione di conferma dell'uscita è obbligatoria per la quotazione nell'App Store di Samsung. Una delle mie app è stata rifiutata per non averlo.
John Ashmore,

6
È odioso, @ Samsung.
dokkaebi,

3
Dipende da cosa vuoi fare all'uscita. Se lo stato viene conservato e puoi tornare indietro, potrebbe non essere necessario mostrare una finestra di dialogo. Tuttavia, in una delle mie applicazioni pulisco i cookie, i dati e chiudo definitivamente le connessioni con un commit finale dei dati. L'utente dovrebbe essere consapevole della finalità della sua scelta, soprattutto perché oggi non è comune avere quel senso di finalità in un'applicazione mobile.
Eric,

15
@ Tom R: totalmente in disaccordo. Ci sono app che non vuoi finire per caso. Lavorano in ambiente aziendale e così via.
TomeeS

Risposte:


390

In Android 2.0+ dovrebbe apparire come:

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

Nelle versioni precedenti sarebbe simile a:

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

}

15
Sempre nella versione 2.0 e successive c'è un nuovo evento onBackPressed che è raccomandato su onKeyDown developer.android.com/intl/zh-TW/reference/android/app/… C'è una sezione qui che parla delle modifiche e del nuovo approccio raccomandato. developer.android.com/intl/zh-TW/sdk/android-2.0.html
Patrick Kafka,

3
Post di blog sulla cattura del tasto Indietro qui: android-developers.blogspot.com/2009/12/… Nota che questo non ti consente di catturare altri modi in cui l'utente può lasciare la tua app: premendo Home, selezionando una notifica, ricevendo un telefono chiama, ecc.
hackbod,

Funziona perfettamente, ma come si fa a interrompere l'esecuzione del codice mentre viene mostrato all'utente?
anon58192932

trovato, questa è una grande soluzione qui: stackoverflow.com/questions/4381296/...
anon58192932

1
Questo è il metodo () quello che sto cercando, ma non sapevo come chiamare questo metodo ().
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();
}

5
Un commento è stato fatto da elBradford: chiamare il super onBackPressed è meglio che supporre che onBackPressed chiamerà solo finish (). Anche se questo è vero ora, potrebbe non essere vero nelle future APICustomTabActivity.super.onBackPressed
mplungjan il

@mplungjan Puoi chiarire il tuo commento ... Stai dicendo che è meglio sostituire il finish()codice con super.onBackPressed()?
divieto di geoingegneria

Il commento ha 3 anni. Non ho idea di cosa sia una buona pratica nel 2015
mplungjan,

@mplungjan Il tuo commento si riferiva ad API future, ma sarebbe utile se tu potessi chiarire comunque.
divieto di geoingegneria,

Nessun problema. Bene, ho appena provato MyActivity.super.onBackPressed();e funziona bene per me. Sembra anche l'approccio più logico: chiamare il metodo che stai scavalcando se vuoi il comportamento standard quando l'utente tocca "No".
divieto di geoingegneria

33

Ho modificato il codice @ user919216 .. e reso compatibile con 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();
    }

}

22

Preferirei uscire con un doppio tocco sul pulsante Indietro rispetto a una finestra di uscita.

In questa soluzione, mostra un brindisi quando torni per la prima volta, avvisando che un'altra back press chiuderà l'App. In questo esempio meno di 4 secondi.

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 da: http://www.androiduipatterns.com/2011/03/back-button-behavior.html


Questo è sicuramente il mio preferito in quanto la finestra di dialogo può essere ingombrante. L'ho modificato leggermente per renderlo 3 secondi e sembra un po 'più naturale con quel marker. Grazie per il post.
PGMacDesign

su doppio back press idealmente l'app dovrei uscire ma nel mio codice è aperta un'attività di login (significa back attività con spinner abilitato)
techDigi

21

Se non si è sicuri che la chiamata a "back" esca dall'app o porti l'utente a un'altra attività, è possibile racchiudere le risposte precedenti in un segno di spunta, isTaskRoot (). Questo può accadere se la tua attività principale può essere aggiunta più volte allo stack posteriore o se stai modificando la cronologia dello stack posteriore.

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

5

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

Devi anche impostare un linguaggio di livello per supportare java 8 nel tuo gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}

5

in Cina, la maggior parte delle app confermerà l'uscita "facendo doppio clic":

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

5

Prima rimuovere super.onBackPressed();dal onbackPressed()metodo rispetto e sotto il codice:

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

}

2

Mi piace un approccio @GLee e usarlo con frammenti come di seguito.

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

Finestra di dialogo usando Frammento:

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

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

y prima attività, non posso metterlo in attività a metà. perché ora chiude l'attività corrente e mostra la precedente
shareef

1

Un'altra alternativa potrebbe essere quella di mostrare a Toast/ Snackbaralla prima stampa posteriore che chiede di premere nuovamente su Esci , che è molto meno invadente rispetto alla visualizzazione di una AlertDialogconferma se l'utente desidera uscire dall'app.

È possibile utilizzare il DoubleBackPress Android Libraryper ottenere ciò con poche righe di codice. Esempio GIF che mostra un comportamento simile.

Per cominciare, aggiungi la dipendenza alla tua applicazione:

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

Successivamente, nella tua attività, implementa il comportamento richiesto.

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

Infine, impostalo come comportamento alla pressione di ritorno.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.