Предотвращение закрытия диалога при повороте экрана в Android - PullRequest
87 голосов
/ 26 сентября 2011

Я пытаюсь предотвратить закрытие диалогов, созданных с помощью Alert Builder, при перезапуске действия.

Если я перегружу метод onConfigurationChanged, я смогу сделать это успешно и сбросить макет до правильной ориентации, но я потеряю функцию закрепления текста в тексте редактирования. Таким образом, в решении проблемы диалога я создал эту проблему редактирования текста.

Если я сохраню строки из текста редактирования и переназначу их при изменении конфигурации onCofiguration, они по-прежнему будут иметь начальное значение, а не то, которое было введено до поворота. Даже если я заставлю недействительного сделать это, похоже, обновит их.

Мне действительно нужно решить либо проблему с диалогом, либо проблему редактирования текста.

Спасибо за помощь.

Ответы [ 11 ]

121 голосов
/ 31 марта 2013

В настоящее время лучший способ избежать этой проблемы - использовать DialogFragment.

Создать новый класс, который расширяет DialogFragment.Переопределите onCreateDialog и верните свой старый Dialog или AlertDialog.

Затем вы можете показать его с помощью DialogFragment.show(fragmentManager, tag).

Вот пример с Listener:

public class MyDialogFragment extends DialogFragment {

    public interface YesNoListener {
        void onYes();

        void onNo();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof YesNoListener)) {
            throw new ClassCastException(activity.toString() + " must implement YesNoListener");
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.dialog_my_title)
                .setMessage(R.string.dialog_my_message)
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onYes();
                    }
                })
                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ((YesNoListener) getActivity()).onNo();
                    }
                })
                .create();
    }
}

А в действии, которое вы называете:

new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+

Этот ответ помогает объяснить эти три других вопроса (и ответы на них):

44 голосов
/ 05 декабря 2014
// Prevent dialog dismiss when orientation changes
private static void doKeepDialog(Dialog dialog){
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
    lp.copyFrom(dialog.getWindow().getAttributes());
    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    dialog.getWindow().setAttributes(lp);
}
public static void doLogout(final Context context){     
        final AlertDialog dialog = new AlertDialog.Builder(context)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.titlelogout)
        .setMessage(R.string.logoutconfirm)
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
        {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ...   
            }

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

        doKeepDialog(dialog);
    }
4 голосов
/ 27 июня 2016

Просто добавьте android: configChanges = "direction" с вашей активностью элемент в AndroidManifest.xml

Пример:

<activity
            android:name=".YourActivity"
            android:configChanges="orientation"
            android:label="@string/app_name"></activity>
4 голосов
/ 26 сентября 2011

Если вы меняете компоновку при изменении ориентации, я бы не поместил android:configChanges="orientation" в манифест, потому что вы все равно воссоздали виды.

Сохраните текущее состояние вашей деятельности (например, введенный текст, отображаемое диалоговое окно, отображаемые данные и т. Д.), Используя следующие методы:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
}

Таким образом, действие снова проходит через onCreate, а затем вызывает метод onRestoreInstanceState, где вы можете снова установить значение EditText.

Если вы хотите хранить более сложные объекты, вы можете использовать

* * 1010

Здесь вы можете сохранить любой объект, и в onCreate вам просто нужно вызвать getLastNonConfigurationInstance();, чтобы получить объект.

1 голос
/ 17 сентября 2018

Кажется, что это все еще проблема, даже если "все делать правильно" и использовать DialogFragment и т. Д.

В Google Issue Tracker есть ветка, которая утверждает, что онаэто связано со старым сообщением об отклонении, оставленным в очереди сообщений.Предложенный обходной путь довольно прост:

    @Override
    public void onDestroyView() {
        /* Bugfix: https://issuetracker.google.com/issues/36929400 */
        if (getDialog() != null && getRetainInstance())
            getDialog().setDismissMessage(null);

        super.onDestroyView();
    }

Невероятно, что это все еще необходимо через 7 лет после того, как об этой проблеме впервые сообщили.

1 голос
/ 13 января 2018

Определенно, лучший подход - использование DialogFragment.

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

public class DialogWrapper extends DialogFragment {
    private static final String ARG_DIALOG_ID = "ARG_DIALOG_ID";

    private int mDialogId;

    /**
     * Display dialog fragment.
     * @param invoker  The fragment which will serve as {@link AlertDialog} alert dialog provider
     * @param dialogId The ID of dialog that should be shown
     */
    public static <T extends Fragment & DialogProvider> void show(T invoker, int dialogId) {
        Bundle args = new Bundle();
        args.putInt(ARG_DIALOG_ID, dialogId);
        DialogWrapper dialogWrapper = new DialogWrapper();
        dialogWrapper.setArguments(args);
        dialogWrapper.setTargetFragment(invoker, 0);
        dialogWrapper.show(invoker.getActivity().getSupportFragmentManager(), null);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDialogId = getArguments().getInt(ARG_DIALOG_ID);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return getDialogProvider().getDialog(mDialogId);
    }

    private DialogProvider getDialogProvider() {
        return (DialogProvider) getTargetFragment();
    }

    public interface DialogProvider {
        Dialog getDialog(int dialogId);
    }
}

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

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

public class MainFragment extends Fragment implements DialogWrapper.DialogProvider {
    private static final int ID_CONFIRMATION_DIALOG = 0;

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        Button btnHello = (Button) view.findViewById(R.id.btnConfirm);
        btnHello.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DialogWrapper.show(MainFragment.this, ID_CONFIRMATION_DIALOG);
            }
        });
    }

    @Override
    public Dialog getDialog(int dialogId) {
        switch (dialogId) {
            case ID_CONFIRMATION_DIALOG:
                return createConfirmationDialog(); //Your AlertDialog
            default:
                throw new IllegalArgumentException("Unknown dialog id: " + dialogId);
        }
    }
}

Вы можете прочитать полную статью в моем блоге Как предотвратить закрытие Dialog? и поиграть с исходным кодом .

1 голос
/ 23 июня 2016

На этот вопрос давно ответили.

И все же это нехакерское и простое решение, которое я использую для себя.

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

Использование:

PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_REQUEST_CODE,
        R.string.message_text,
        R.string.positive_btn_text,
        R.string.negative_btn_text)
        .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);

Или

 PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_EXPLAIN_LOCATION,
        "Dialog title", 
        "Dialog Message", 
        "Positive Button", 
        "Negative Button", 
        false)
    .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);





public class ExampleActivity extends Activity implements PersistentDialogListener{

        @Override
        void onDialogPositiveClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }
        }

        @Override
        void onDialogNegativeClicked(int requestCode) {
                switch(requestCode) {
                  case RC_REQUEST_CODE:
                  break;
                }          
        }
}
1 голос
/ 04 февраля 2015

Вы можете объединить методы onSave / onRestore диалога с методами onSave / onRestore действия Activity для сохранения состояния диалога.

Примечание: Этот метод работает для таких «простых» диалогов, как отображение сообщения с предупреждением. Он не будет воспроизводить содержимое WebView, встроенного в диалог. Если вы действительно хотите предотвратить закрытие сложного диалога во время ротации, попробуйте метод Chung IW.

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
     super.onRestoreInstanceState(savedInstanceState);
     myDialog.onRestoreInstanceState(savedInstanceState.getBundle("DIALOG"));
     // Put your codes to retrieve the EditText contents and 
     // assign them to the EditText here.
}

@Override
protected void onSaveInstanceState(Bundle outState) {
     super.onSaveInstanceState(outState);
     // Put your codes to save the EditText contents and put them 
     // to the outState Bundle here.
     outState.putBundle("DIALOG", myDialog.onSaveInstanceState());
}
1 голос
/ 06 февраля 2012

Очень простой подход - создать диалоги из метода onCreateDialog() (см. Примечание ниже). Вы показываете их через showDialog(). Таким образом, Android обрабатывает ротацию за вас, и вам не нужно вызывать dismiss() в onPause(), чтобы избежать WindowLeak, и тогда вам также не придется восстанавливать диалог. Из документов:

Показать диалог, управляемый этим действием. Вызов onCreateDialog (int, Bundle) будет выполнен с тем же идентификатором при первом вызове для данного идентификатора. После этого диалоговое окно будет автоматически сохранено и восстановлено.

См. Документы Android showDialog () для получения дополнительной информации. Надеюсь, это кому-нибудь поможет!

Примечание: Если используется AlertDialog.Builder, не вызывайте show() из onCreateDialog(), вместо этого позвоните create(). Если вы используете ProgressDialog, просто создайте объект, установите нужные параметры и верните его. В заключение, show() внутри onCreateDialog() вызывает проблемы, просто создайте экземпляр de Dialog и верните его. Это должно работать! (У меня возникли проблемы с использованием showDialog () из onCreate () - фактически, не показывая диалоговое окно, - но если вы используете его в onResume () или в обратном вызове слушателя, это работает хорошо).

0 голосов
/ 30 января 2018

У меня была похожая проблема: при изменении ориентации экрана вызывался прослушиватель onDismiss диалогового окна, даже если пользователь не закрывал диалоговое окно.Я смог обойти эту проблему, используя вместо этого onCancel слушатель, который срабатывал как при нажатии кнопки «Назад», так и при нажатии пользователем вне диалогового окна.

...