web-dev-qa-db-fra.com

Empêcher le renvoi du dialogue lors de la rotation de l'écran sous Android

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.

84
draksia

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):

118
Brais Gabin
// 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);
    }
43
Chung IW

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.

4
Maria Neumayer

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>
3
SAAM

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.

1
Magnus W

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());
}
1
hackjutsu

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).

1
Caumons

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;
                }          
        }
}
1
Ioane Sharvadze

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.

0
Sam

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 .

0
Dmitry Korobeinikov