web-dev-qa-db-fra.com

Comment conserver correctement un DialogFragment par rotation?

J'ai une FragmentActivity qui héberge un DialogFragment.

Le DialogFragment effectue les requêtes réseau et gère l'authentification Facebook, je dois donc le conserver pendant la rotation.

J'ai lu toutes les autres questions relatives à ce problème, mais aucune n'a réellement résolu le problème.

J'utilise putFragment et getFragment pour enregistrer l'instance de fragment et la récupérer à nouveau lors de la recréation de l'activité.

Cependant, je reçois toujours une exception de pointeur nul sur l'appel à getFragment dans onRestoreInstanceState. Je voudrais également empêcher la fermeture de la boîte de dialogue lors de la rotation, mais jusqu'à présent, je ne peux même pas en conserver l'instance.

Des idées ce qui ne va pas?

Voici à quoi ressemble actuellement mon code:

public class OKLoginActivity extends FragmentActivity implements OKLoginDialogListener
{

    private OKLoginFragment loginDialog;
    private static final String TAG_LOGINFRAGMENT = "OKLoginFragment";


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fm = getSupportFragmentManager();

        if(savedInstanceState == null)
        {
            loginDialog = new OKLoginFragment(); 
            loginDialog.show(fm, TAG_LOGINFRAGMENT);
        }
    }


    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        getSupportFragmentManager().putFragment(outState,TAG_LOGINFRAGMENT, loginDialog);
    }

    @Override
    public void onRestoreInstanceState(Bundle inState)
    {
        FragmentManager fm = getSupportFragmentManager();
        loginDialog = (OKLoginFragment) fm.getFragment(inState, TAG_LOGINFRAGMENT);
    }

}

Il s'agit de la trace de pile d'exceptions:

02-01 16:31:13.684: E/AndroidRuntime(9739): FATAL EXCEPTION: main
02-01 16:31:13.684: E/AndroidRuntime(9739): Java.lang.RuntimeException: Unable to start activity ComponentInfo{io.openkit.example.sampleokapp/io.openkit.OKLoginActivity}: Java.lang.NullPointerException
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2180)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2230)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.handleRelaunchActivity(ActivityThread.Java:3692)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.access$700(ActivityThread.Java:141)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1240)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.os.Handler.dispatchMessage(Handler.Java:99)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.os.Looper.loop(Looper.Java:137)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.main(ActivityThread.Java:5039)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Java.lang.reflect.Method.invokeNative(Native Method)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Java.lang.reflect.Method.invoke(Method.Java:511)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:793)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:560)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at dalvik.system.NativeStart.main(Native Method)
02-01 16:31:13.684: E/AndroidRuntime(9739): Caused by: Java.lang.NullPointerException
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.Java:528)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at io.openkit.OKLoginActivity.onRestoreInstanceState(OKLoginActivity.Java:62)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.Activity.performRestoreInstanceState(Activity.Java:910)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.Java:1131)
02-01 16:31:13.684: E/AndroidRuntime(9739):     at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2158)
70
ch3rryc0ke

Dans votre DialogFragment, appelez Fragment.setRetainInstance(boolean) avec la valeur true. Vous n'avez pas besoin d'enregistrer le fragment manuellement, le framework s'occupe déjà de tout cela. L'appel empêchera la destruction de votre fragment lors de la rotation et vos requêtes réseau ne seront pas affectées.

Vous devrez peut-être ajouter ce code pour empêcher la fermeture de votre boîte de dialogue lors de la rotation, en raison d'un bug avec la bibliothèque de compatibilité:

@Override
public void onDestroyView() {
    Dialog dialog = getDialog();
    // handles https://code.google.com/p/Android/issues/detail?id=17423
    if (dialog != null && getRetainInstance()) {
        dialog.setDismissMessage(null);
    }
    super.onDestroyView();
}
138
antonyt

Un des avantages de l'utilisation de dialogFragment par rapport à la simple utilisation de alertDialogBuilder est précisément parce que dialogfragment peut se recréer automatiquement lors de la rotation sans intervention de l'utilisateur.

Cependant, lorsque la boîte de dialogue ne se recrée pas, il est possible que vous écrasiez onSaveInstanceState mais que vous n'ayez pas appelé super:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState); // <-- must call this if you want to retain dialogFragment upon rotation
    ...
}
15
Neoh

Il s'agit d'une méthode pratique utilisant le correctif de la réponse d'Antonyt:

public class RetainableDialogFragment extends DialogFragment {

    public RetainableDialogFragment() {
        setRetainInstance(true);
    }

    @Override
    public void onDestroyView() {
        Dialog dialog = getDialog();
        // handles https://code.google.com/p/Android/issues/detail?id=17423
        if (dialog != null && getRetainInstance()) {
            dialog.setDismissMessage(null);
        }
        super.onDestroyView();
    }
}

Laissez simplement votre DialogFragment étendre cette classe et tout ira bien. Cela devient particulièrement pratique si vous avez plusieurs DialogFragments dans votre projet qui ont tous besoin de ce correctif.

9
Willi Mentzel