Próbuję zapobiec zamykaniu okien dialogowych utworzonych za pomocą narzędzia Alert Builder po ponownym uruchomieniu działania.
Jeśli przeciążę metodę onConfigurationChanged, mogę z powodzeniem to zrobić i zresetować układ do prawidłowej orientacji, ale stracę funkcję lepkiego tekstu w edytorze. Więc rozwiązując problem z dialogiem stworzyłem ten problem z edittextem.
Jeśli zapiszę ciągi z edycji edittext i przypiszę je ponownie w zmianie onCofiguration, nadal wydaje się, że domyślnie mają one wartość początkową, a nie to, co zostało wprowadzone przed rotacją. Nawet jeśli wymuszę, unieważniony wydaje się je aktualizować.
Naprawdę muszę rozwiązać problem z dialogiem lub z edittextem.
Dzięki za pomoc.
Odpowiedzi:
Obecnie najlepszym sposobem uniknięcia tego problemu jest użycie pliku
DialogFragment
.Utwórz nową klasę, która rozszerza
DialogFragment
. ZastąponCreateDialog
i zwróć staryDialog
lub plikAlertDialog
.Następnie możesz to pokazać za pomocą
DialogFragment.show(fragmentManager, tag)
.Oto przykład z
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(); } }
A w wywołanym działaniu:
new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+
Ta odpowiedź pomaga wyjaśnić te pozostałe trzy pytania (i ich odpowiedzi):
źródło
onAttach
jest teraz przestarzałe. Co należy zrobić zamiast tego?onAttach(Context context)
iandroid.support.v4.app.DialogFragment
.onAttach
Metoda bierzecontext
zamiastactivity
jako parametr teraz.YesNoListener
Jednak prawdopodobnie nie ma takiej potrzeby . Zobacz tę odpowiedź .// 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); }
źródło
Context
wewnątrz przycisków dialogowychOnClickListener
.Jeśli zmieniasz układ przy zmianie orientacji, nie umieszczałbym
android:configChanges="orientation"
w twoim manifeście, ponieważ i tak odtwarzasz widoki.Zapisz aktualny stan swojej aktywności (np. Wprowadzony tekst, wyświetlone okno dialogowe, wyświetlone dane itp.), Korzystając z następujących metod:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); }
W ten sposób działanie przechodzi ponownie przez onCreate, a następnie wywołuje metodę onRestoreInstanceState, w której można ponownie ustawić wartość EditText.
Jeśli chcesz przechowywać bardziej złożone obiekty, możesz użyć
@Override public Object onRetainNonConfigurationInstance() { }
Tutaj możesz przechowywać dowolny obiekt, aw onCreate wystarczy wywołać,
getLastNonConfigurationInstance();
aby uzyskać Object.źródło
OnRetainNonConfigurationInstance()
jest teraz przestarzały, jak mówi dokument: developer.android.com/reference/android/app/…setRetainInstance(boolean retain)
należy zamiast tego użyć: developer.android.com/reference/android/app/…setRetainInstance
jest zupełnie inny: dotyczy fragmentów i nie gwarantuje, że instancja zostanie zachowana.Po prostu dodaj android: configChanges = "orientacja" z elementem aktywności w AndroidManifest.xml
Przykład:
<activity android:name=".YourActivity" android:configChanges="orientation" android:label="@string/app_name"></activity>
źródło
Bardzo prostym podejściem jest utworzenie okien dialogowych z metody
onCreateDialog()
(patrz uwaga poniżej). Pokazujesz imshowDialog()
. W ten sposób, Android obsługuje obrót dla Ciebie i nie trzeba zadzwonićdismiss()
wonPause()
celu uniknięcia WindowLeak i wtedy nie trzeba przywrócić okno. Z dokumentów:Aby uzyskać więcej informacji, zobacz dokumentację dotyczącą systemu Android showDialog () . Mam nadzieję, że to komuś pomoże!
Uwaga: jeśli używasz AlertDialog.Builder, nie dzwoń
show()
zonCreateDialog()
,create()
zamiast tego dzwoń . Jeśli używasz ProgressDialog, po prostu utwórz obiekt, ustaw potrzebne parametry i zwróć go. Podsumowując,show()
wnętrzeonCreateDialog()
powoduje problemy, po prostu stwórz instancję de Dialog i zwróć ją. To powinno działać! (Doświadczyłem problemów z używaniem showDialog () z onCreate () - właściwie nie pokazuję okna dialogowego - ale jeśli używasz go w onResume () lub w wywołaniu zwrotnym odbiornika, działa to dobrze).źródło
AlertDialog.Builder
. Jeśli użyjesz go wewnątrzonCreateDialog()
, zamiast użyć,show()
zwróć wynikcreate()
. W przeciwnym razie wywołajshow()
i zapisz zwrócony AlertDialog w atrybucie działania i wonPause()
dismiss()
nim, jeśli jest wyświetlany, aby uniknąć WindowLeak. Mam nadzieję, że to pomoże!Na to pytanie odpowiedziano dawno temu.
Jednak jest to niehackowe i proste rozwiązanie, którego używam dla siebie.
Zrobiłem tę klasę pomocniczą dla siebie, więc możesz jej użyć również w swojej aplikacji.
Wykorzystanie to:
PersistentDialogFragment.newInstance( getBaseContext(), RC_REQUEST_CODE, R.string.message_text, R.string.positive_btn_text, R.string.negative_btn_text) .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);
Lub
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; } } }
źródło
Możesz łączyć metody onSave / onRestore Dialog z metodami onSave / onRestore działania , aby zachować stan okna dialogowego.
Uwaga: ta metoda działa w przypadku „prostych” okien dialogowych, takich jak wyświetlanie komunikatu ostrzegawczego. Nie odtworzy zawartości WebView osadzonej w oknie dialogowym. Jeśli naprawdę chcesz zapobiec blokowaniu złożonych dialogów podczas rotacji, wypróbuj metodę 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()); }
źródło
Zdecydowanie najlepszym podejściem jest użycie DialogFragment.
Oto moje rozwiązanie klasy wrapper, które pomaga zapobiegać odrzucaniu różnych okien dialogowych w jednym fragmencie (lub działaniu z małą refaktoryzacją). Pomaga to również uniknąć masowego refaktoryzacji kodu, jeśli z jakichś powodów jest dużo
AlertDialogs
rozproszonego kodu z niewielkimi różnicami między nimi pod względem działań, wyglądu lub czegoś innego.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); } }
Jeśli chodzi o aktywność, możesz wywołać ją w
getContext()
środkuonCreateDialog()
, przesłać ją doDialogProvider
interfejsu i zażądać określonego okna dialogowego przezmDialogId
. Należy usunąć całą logikę postępowania z fragmentem docelowym.Wykorzystanie z fragmentu:
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); } } }
Możesz przeczytać cały artykuł na moim blogu Jak zapobiec zamknięciu Dialogu? i baw się kodem źródłowym .
źródło
Wygląda na to, że jest to nadal problem, nawet jeśli „robisz wszystko dobrze” i używasz
DialogFragment
itp.W Google Issue Tracker istnieje wątek, który twierdzi, że jest to spowodowane pozostawieniem starej wiadomości o odrzuceniu w kolejce wiadomości. Podane obejście jest dość proste:
@Override public void onDestroyView() { /* Bugfix: https://issuetracker.google.com/issues/36929400 */ if (getDialog() != null && getRetainInstance()) getDialog().setDismissMessage(null); super.onDestroyView(); }
Niesamowite, że jest to nadal potrzebne 7 lat po pierwszym zgłoszeniu tego problemu.
źródło
Miałem podobny problem: kiedy zmieniła się orientacja ekranu,
onDismiss
wywoływano detektor okna dialogowego, mimo że użytkownik nie zamknął okna. Udało mi się obejść ten problem, używając zamiast tegoonCancel
detektora, który uruchamiał się zarówno wtedy, gdy użytkownik nacisnął przycisk Wstecz, jak i gdy użytkownik dotknął poza oknem dialogowym.źródło
Po prostu użyj
ConfigurationChanges = Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize
a aplikacja będzie wiedzieć, jak obsługiwać obrót i rozmiar ekranu.
źródło