J'essaie d'empêcher que les dialogues construits avec le générateur Alert soient fermés lorsque l'activité est redémarrée.
Si je surcharge la méthode onConfigurationChanged, je peux le faire avec succès et réinitialiser la présentation pour corriger l'orientation, mais je perds la fonctionnalité de texte collant d'edittext. Donc, en résolvant le problème de dialogue, j'ai créé ce problème d'edittext.
Si je sauvegarde les chaînes de l'edittext et que je les réaffecte lors de la modification onCofiguration, elles semblent toujours revenir à la valeur initiale, et non à celle entrée avant la rotation. Même si je force un invalide ne semble les mettre à jour.
J'ai vraiment besoin de résoudre le problème de dialogue ou le problème d'edittext.
Merci pour l'aide.
Le meilleur moyen d'éviter ce problème aujourd'hui est d'utiliser un DialogFragment
.
Créez une nouvelle classe qui étend DialogFragment
. Remplacez onCreateDialog
et renvoyez votre ancien Dialog
ou un AlertDialog
.
Ensuite, vous pouvez le montrer avec DialogFragment.show(fragmentManager, tag)
.
Voici un exemple avec un 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();
}
}
Et dans l'activité que vous appelez:
new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+
Cette réponse aide à expliquer ces trois autres questions (et leurs réponses):
// 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);
}
Si vous modifiez la présentation lors du changement d'orientation, je ne mettrais pas Android:configChanges="orientation"
dans votre manifeste, car vous recréez de toute façon les vues.
Enregistrez l’état actuel de votre activité (texte saisi, boîte de dialogue affichée, données affichées, etc.) en utilisant les méthodes suivantes:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
De cette façon, l'activité reprend onCreate et appelle ensuite la méthode onRestoreInstanceState dans laquelle vous pouvez redéfinir votre valeur EditText.
Si vous souhaitez stocker des objets plus complexes, vous pouvez utiliser
@Override
public Object onRetainNonConfigurationInstance() {
}
Ici, vous pouvez stocker n'importe quel objet et dans onCreate, il vous suffit d'appeler getLastNonConfigurationInstance();
pour obtenir l'objet.
Ajoutez simplement Android: configChanges = "orientation" à votre activité élément dans AndroidManifest.xml
Exemple:
<activity
Android:name=".YourActivity"
Android:configChanges="orientation"
Android:label="@string/app_name"></activity>
Il semble que cela reste un problème, même lorsque "tout est bien" et que l'on utilise DialogFragment
etc.
Il existe un fil de discussion sur Google Issue Tracker , qui affirme que cela est dû à un ancien message de renvoi laissé dans la file d'attente. La solution fournie est assez simple:
@Override
public void onDestroyView() {
/* Bugfix: https://issuetracker.google.com/issues/36929400 */
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
Incroyable que cela soit encore nécessaire 7 ans après que ce problème a été signalé pour la première fois.
Vous pouvez combiner les méthodes onSave/onRestore du dialogue de dialogue avec les méthodes onSave/onRestore de l'activité pour conserver l'état du dialogue.
Note: Cette méthode fonctionne pour ces dialogues "simples", tels que l'affichage d'un message d'alerte. Il ne reproduira pas le contenu d'une WebView incorporée dans un dialogue. Si vous voulez vraiment empêcher un dialogue complexe de se fermer pendant la rotation, essayez la méthode de 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());
}
Une approche très simple consiste à créer les boîtes de dialogue à partir de la méthode onCreateDialog()
(voir la remarque ci-dessous). Vous leur montrez à travers showDialog()
. De cette façon, Android gère la rotation pour vous et vous n'avez pas à appeler dismiss()
dans onPause()
pour éviter un WindowLeak et vous ne devez pas non plus restaurer la boîte de dialogue. De la docs:
Afficher une boîte de dialogue gérée par cette activité. Un appel à onCreateDialog (int, Bundle) sera effectué avec le même identifiant la première fois qu'il est appelé pour un identifiant donné. Par la suite, la boîte de dialogue sera automatiquement enregistrée et restaurée.
Voir Documentation Android showDialog () pour plus d'informations. J'espère que ça aide quelqu'un!
Remarque: Si vous utilisez AlertDialog.Builder, n'appelez pas show()
à partir de onCreateDialog()
, appelez plutôt create()
. Si vous utilisez ProgressDialog, créez simplement l’objet, définissez les paramètres nécessaires et renvoyez-le. En conclusion, show()
dans onCreateDialog()
pose des problèmes, créez simplement une instance de Dialog et renvoyez-la. Cela devrait marcher! (J'ai rencontré des problèmes lors de l'utilisation de showDialog () de onCreate (), qui n'affiche pas le dialogue, mais si vous l'utilisez dans onResume () ou dans un rappel d'écoute, cela fonctionne bien).
Cette question a reçu une réponse il y a longtemps.
Pourtant, c’est la solution non-hacky et simple que j’utilise moi-même.
J'ai moi-même cette classe d'assistance , vous pouvez donc l'utiliser également dans votre application.
L'utilisation est:
PersistentDialogFragment.newInstance(
getBaseContext(),
RC_REQUEST_CODE,
R.string.message_text,
R.string.positive_btn_text,
R.string.negative_btn_text)
.show(getSupportFragmentManager(), PersistentDialogFragment.TAG);
Ou
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;
}
}
}
J'ai eu un problème similaire: lorsque l'orientation de l'écran a changé, le programme d'écoute onDismiss
de la boîte de dialogue a été appelé, même si l'utilisateur n'a pas ignoré la boîte de dialogue. J'ai pu contourner ce problème en utilisant plutôt le programme d'écoute onCancel
, qui était déclenché lorsque l'utilisateur appuyait sur le bouton Précédent et lorsque l'utilisateur appuyait en dehors de la boîte de dialogue.
Décidément, la meilleure approche consiste à utiliser DialogFragment.
Voici la solution mienne de la classe wrapper qui permet d’empêcher que différents dialogues soient ignorés au sein d’un fragment (ou d’une activité avec un petit refactoring). En outre, il est utile d'éviter une refactorisation massive du code si, pour certaines raisons, il y a beaucoup de AlertDialogs
dispersés dans le code avec de légères différences entre eux en termes d'actions, d'apparence ou autre.
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);
}
}
En ce qui concerne Activity, vous pouvez appeler getContext()
à l'intérieur de onCreateDialog()
, transposez-le sur l'interface DialogProvider
et demandez un dialogue spécifique par mDialogId
. Toute la logique pour traiter un fragment cible doit être supprimée.
Utilisation de fragment:
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);
}
}
}
Vous pouvez lire l'article complet sur mon blog Comment éviter que Dialog ne soit rejeté? et jouez avec le code source .