Предотвращение отключения диалога при вращении экрана в Android
Я пытаюсь предотвратить диалоги, построенные с помощью Alert builder, от увольнения при перезапуске действия.
Если я перегружаю метод onConfigurationChanged, я могу успешно сделать это и сбросить макет для правильной ориентации, но я теряю функцию липкого текста edittext. Поэтому при решении проблемы диалога я создал эту проблему edittext.
Если я сохраняю строки из edittext и переназначаю их в изменении onCofiguration, они все равно кажутся по умолчанию начальное значение не то, что было введено до поворота. Даже если я заставляю invalidate, похоже, обновляет их.
Мне действительно нужно решить либо проблему диалога, либо проблему edittext.
Спасибо за помощь.
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()); }
Кажется, что это все еще проблема, даже когда "делать все правильно" и использовать
DialogFragmentetc.есть нить на 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