Как показать диалоговое окно, чтобы подтвердить, что пользователь хочет выйти из Android Activity?

274

Я пытался показать "Вы хотите выйти?" тип диалога, когда пользователь пытается выйти из Activity.

Однако я не могу найти соответствующие хуки API. Activity.onUserLeaveHint()Первоначально выглядело многообещающе, но я не могу найти способ остановить деятельность.

Петр А
источник
39
Это абсолютно необходимо, чтобы у вас было это приглашение? Когда пользователь хочет завершить действие, он должен иметь возможность сделать это немедленно. Вы можете пересмотреть свою стратегию.
Том Р
4
Отображение опции подтверждения выхода обязательно для внесения в список в магазине приложений Samsung. Одно из моих приложений было отклонено из-за отсутствия этого.
Джон Эшмор
6
Это противно, @Samsung.
Dokkaebi
3
Это зависит от того, что вы хотите сделать при выходе. Если состояние сохраняется, и вы можете просто вернуться назад, показ диалога может не потребоваться. Однако в одном из моих приложений я очищаю файлы cookie, данные и закрываю соединения навсегда с окончательной фиксацией данных. Пользователи должны быть осведомлены о окончательности своего выбора, особенно потому, что сегодня такое чувство завершенности в мобильном приложении встречается редко.
Эрик
15
@ Том Р: Абсолютно не согласен. Есть приложения, которые вы не хотите завершать случайно. Они работают в бизнес-среде и так далее.
TomeeNS

Ответы:

390

В Android 2.0+ это будет выглядеть так:

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

В более ранних версиях это выглядело бы так:

@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
источник
15
Также в версии 2.0 и выше есть новое событие onBackPressed, которое рекомендуется для onKeyDown developer.android.com/intl/zh-TW/reference/android/app/… Здесь есть раздел, в котором говорится об изменениях и новом рекомендуемом подходе. developer.android.com/intl/zh-TW/sdk/android-2.0.html
Патрик Кафка
3
Сообщение в блоге о перехвате клавиши «Назад» здесь: android-developers.blogspot.com/2009/12/… Обратите внимание, что это не позволяет вам поймать другие способы, которыми пользователь может покинуть ваше приложение: нажатие дома, выбор уведомления, получение телефона звонок и т. д.
hackbod
Это прекрасно работает, но как вы заставляете выполнение кода останавливаться, пока он показывается пользователю?
anon58192932
нашел это, это отличное решение здесь: stackoverflow.com/questions/4381296/…
anon58192932
1
Это метод (), который я ищу, но я не знал, где вызвать этот метод ().
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
ElBradford сделал комментарий: вызывать super onBackPressed лучше, чем предполагать, что onBackPressed будет вызывать только finish (). Даже если это правда сейчас, это может быть не так в будущих APICustomTabActivity.super.onBackPressed
mplungjan
@mplungjan Можете ли вы уточнить свой комментарий ... Вы говорите, что лучше заменить finish()код super.onBackPressed()?
запрет геоинженерии
Комментарий 3 года. Я понятия не имею, что такое хорошая практика в 2015 году
mplungjan
@mplungjan Ваш комментарий относится к будущим API, но было бы полезно, если бы вы все равно могли уточнить.
запрет геоинженерии
Нет проблем. Ну, я только что попробовал, MyActivity.super.onBackPressed();и это прекрасно работает для меня. Это также кажется наиболее логичным подходом - вызывать метод, который вы переопределяете, если вы хотите стандартного поведения, когда пользователь нажимает «Нет».
запрет геоинженерии
33

Изменили код @ user919216 .. и сделали его совместимым с 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

Я предпочел бы выйти с двойным нажатием на кнопку назад, чем с диалогом выхода.

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

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

Жетон от: http://www.androiduipatterns.com/2011/03/back-button-behavior.html

Тони Гамез
источник
Этот, безусловно, мой любимый, так как диалоговое окно может быть громоздким. Я немного изменил его, чтобы сделать его 3 секунды, и этот маркер кажется немного более естественным. Спасибо за пост.
PGMacDesign
при двойном нажатии в идеале приложение должно выйти из моего кода, но в моем коде оно открывает действие входа (означает возвращение активности с включенным вращателем)
techDigi 10.10.16
21

Если вы не уверены, что вызов «назад» выйдет из приложения или переместит пользователя на другое действие, вы можете свернуть вышеуказанные ответы в проверке isTaskRoot (). Это может произойти, если ваша основная деятельность может быть добавлена ​​в задний стек несколько раз, или если вы манипулируете своей историей заднего стека.

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

Используя лямбду:

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

Вам также нужно установить язык уровня для поддержки Java 8 в вашем gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}
vasiljevski
источник
5

В Китае большинство приложений подтвердит выход, нажав «дважды»:

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

Сначала удалите super.onBackPressed();из onbackPressed()метода, чем и ниже код:

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

}
Анджали-Systematix
источник
2

Мне нравится подход @GLee и использование его с фрагментом, как показано ниже.

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

Диалог с использованием фрагмента:

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
источник
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();
                }
            }
        }
    }
Мохит Худа
источник
у первого действия, не могу ли я положить его в деятельность в середине. потому что теперь он закрывает текущую активность и показывает предыдущую
shareef
1

Другой альтернативой может быть показ Toast/ / Snackbarпри первом нажатии назад с просьбой снова вернуться к выходу , что гораздо менее навязчиво, чем показ AlertDialogподтверждения, если пользователь хочет выйти из приложения.

Вы можете использовать DoubleBackPress Android Libraryдля этого несколько строчек кода. Пример GIF, показывающий похожее поведение.

Для начала добавьте зависимость в ваше приложение:

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

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

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

Наконец, установите это как поведение при нажатии на спину.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
Каушик Н.П.
источник