Предотвращение отключения диалога при вращении экрана в Android



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



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



Если я сохраняю строки из edittext и переназначаю их в изменении onCofiguration, они все равно кажутся по умолчанию начальное значение не то, что было введено до поворота. Даже если я заставляю invalidate, похоже, обновляет их.



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



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

644   11  

11 ответов:

лучший способ избежать этой проблемы в наше время с помощью 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+

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

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

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

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

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

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

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

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

@Override
public Object onRetainNonConfigurationInstance() {
}

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

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

пример:

<activity
            android:name=".YourActivity"
            android:configChanges="orientation"
            android:label="@string/app_name"></activity>

на этот вопрос давным-давно был дан ответ.

и все же это non-hacky и простой решение, которое я использую для себя.

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

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

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

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

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

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

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

вы можете комбинировать Диалог onSave / onRestore методы активность onSave / onRestore методы для сохранения состояния диалогового окна.

Примечание: этот метод работает для тех "простых" диалогов, таких как отображение тревожного сообщения. Он не будет воспроизводить содержимое веб-представления, встроенного в диалоговое окно. Если вы действительно хотите предотвратить сложное диалоговое окно от увольнения во время ротации, попробуйте метод 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());
}

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

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

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

        super.onDestroyView();
    }

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

определенно, лучший подход заключается в использовании 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);
        }
    }
}

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

у меня была аналогичная проблема: когда ориентация экрана изменилась, диалог onDismiss прослушиватель был вызван, даже если пользователь не закрыл диалоговое окно. Я смог обойти это с помощью onCancel прослушиватель, который срабатывает как при нажатии пользователем кнопки "назад", так и при прикосновении пользователя за пределами диалогового окна.

просто использовать

ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize

и приложение будет знать, как обрабатывать вращение и размер экрана.

Comments

    Ничего не найдено.