Prevent dialog dismissal on screen rotation in Android

64,044

Solution 1

The best way to avoid this problem nowadays is by using a DialogFragment.

Create a new class which extends DialogFragment. Override onCreateDialog and return your old Dialog or an AlertDialog.

Then you can show it with DialogFragment.show(fragmentManager, tag).

Here's an example with a 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();
    }
}

And in the Activity you call:

new MyDialogFragment().show(getSupportFragmentManager(), "tag"); // or getFragmentManager() in API 11+

This answer helps explain these other three questions (and their answers):

Solution 2

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

Solution 3

If you're changing the layout on orientation change I wouldn't put android:configChanges="orientation" in your manifest because you're recreating the views anyway.

Save the current state of your activity (like text entered, shown dialog, data displayed etc.) using these methods:

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

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

That way the activity goes through onCreate again and afterwards calls the onRestoreInstanceState method where you can set your EditText value again.

If you want to store more complex Objects you can use

@Override
public Object onRetainNonConfigurationInstance() {
}

Here you can store any object and in onCreate you just have to call getLastNonConfigurationInstance(); to get the Object.

Solution 4

Just add android:configChanges="orientation" with your activity element in AndroidManifest.xml

Example:

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

Solution 5

This question was answered a long time ago.

Yet this is non-hacky and simple solution I use for myself.

I did this helper class for myself, so you can use it in your application too.

Usage is:

PersistentDialogFragment.newInstance(
        getBaseContext(),
        RC_REQUEST_CODE,
        R.string.message_text,
        R.string.positive_btn_text,
        R.string.negative_btn_text)
        .show(getSupportFragmentManager(), PersistentDialogFragment.TAG);

Or

 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;
                }          
        }
}
Share:
64,044
draksia
Author by

draksia

Updated on July 08, 2022

Comments

  • draksia
    draksia almost 2 years

    I am trying to prevent dialogs built with Alert builder from being dismissed when the Activity is restarted.

    If I overload the onConfigurationChanged method I can successfully do this and reset the layout to correct orientation but I lose sticky text feature of edittext. So in solving the dialog problem I have created this edittext problem.

    If I save the strings from the edittext and reassign them in the onCofiguration change they still seem to default to initial value not what was entered before rotation. Even if I force an invalidate does seem to update them.

    I really need to solve either the dialog problem or the edittext problem.

    Thanks for the help.

  • Caumons
    Caumons about 12 years
    For which case would you need some code? The onCreateDialog() or showing it with the builder and calling show() to it?
  • neteinstein
    neteinstein about 12 years
    I have managed to do it.. but the thing is, onCreateDialog() is now deprecated :-\
  • Caumons
    Caumons about 12 years
    OK! Have in mind that most of Android devices still work with 2.X versions, so you can use it anyway! Have a look at Android platform versions usage
  • neteinstein
    neteinstein about 12 years
    Still, what's the other option if not onCreateDialog?
  • Caumons
    Caumons about 12 years
    You can use the builder classes e.g. AlertDialog.Builder. If you use it inside onCreateDialog(), instead of using show() return the result of create(). Else, call show() and store the returned AlertDialog into an attribute of the Activity and in onPause() dismiss() it if showing in order to avoid a WindowLeak. Hope it helps!
  • ForceMagic
    ForceMagic about 10 years
    OnRetainNonConfigurationInstance() is now deprecated as the Doc says : developer.android.com/reference/android/app/… setRetainInstance(boolean retain) should be use instead : developer.android.com/reference/android/app/…
  • Chung IW
    Chung IW over 9 years
    To whom find my code not useful, try it before click :-)
  • nmvictor
    nmvictor almost 9 years
    Works, don't know how but it works! Clean simple and abstract solution, thanks.
  • Adrian Olar
    Adrian Olar over 8 years
    Amazing solution, very simple unlike the other solutions I found, thank you so much, you helped me prevent my app from crashing and solved a crucial bug!!!
  • Zuko
    Zuko over 8 years
    Cool stuff @ChungIW Now thats coding I adhore
  • Sharp Edge
    Sharp Edge over 8 years
    This used to work well, now on lollipop it doesn't work on some devices, dialog still disappears (sigh)
  • Alpha Huang
    Alpha Huang about 8 years
    There is a button in my app to call .show(), I have to remember the state of the alert dialog, showing/dismissed. Is there a way to keep the dialog without calling .show()?
  • Stephan Henningsen
    Stephan Henningsen over 7 years
    @ChungIW, Could you please elaborate? Why do Dialogs usually not survive an orientation change and why does this fix it?
  • farahm
    farahm over 7 years
    It says that onAttach is deprecated now. What should be done instead?
  • Stan Mots
    Stan Mots over 7 years
    @faraz_ahmed_kamran, you should use onAttach(Context context) and android.support.v4.app.DialogFragment. The onAttach method takes context instead of activity as a parameter now.
  • Peter Chaula
    Peter Chaula over 7 years
    @StephanHenningsen the Activity gets destroyed on orientation change.
  • Stephan Henningsen
    Stephan Henningsen over 7 years
    @ChungIW The orientation changes - the Activity gets destroyed - the Activity gets recreated - And your code ensure the Dialog gets recreated. How? Why does it work?
  • Miha_x64
    Miha_x64 about 7 years
    @ForceMagic setRetainInstance is completely different: it's for Fragments and it does not guarantee you that instance will be retained.
  • The incredible Jan
    The incredible Jan about 7 years
    I think this code is bad. doLogout() has a reference to context which is/contains the activity. The activity cannot be destroyed which can cause a memory leak. I was looking for a possibility to use AlertDialog from static context but now I'm sure that it is impossible. The result can only be garbage I think.
  • Udo Klimaschewski
    Udo Klimaschewski over 6 years
    It just looks like it works. The dialog stays open, but it has no connection to the newly created activity or fragment (it gets newly created on each orientation change). So you can't do anything that requires a Context inside the dialog buttons OnClickListener.
  • Sam
    Sam over 6 years
    This can cause the dialog to display incorrectly in some circumstances.
  • Hexise
    Hexise about 6 years
    This code works but not recommend at all. It leaks activity reference, which is why the dialog can persistent. This is a very bad practice which will lead to memory leak.
  • tsig
    tsig almost 6 years
    android:configChanges="orientation|screenSize" Note: If your application targets Android 3.2 (API level 13) or higher, then you should also declare the "screenSize" configuration, because it also changes when a device switches between portrait and landscape orientations.
  • Mygod
    Mygod over 5 years
    There's probably no need for YesNoListener though. See this answer.
  • Roshana Pitigala
    Roshana Pitigala about 5 years
    While this may answer the question it's better to add some description on how this answer may help to solve the issue. Please read How do I write a good answer to know more.
  • CoolMind
    CoolMind about 4 years
  • Luis A. Florit
    Luis A. Florit over 2 years
    Didn't work for me on Android 11.