Pourquoi Fragment ne garde pas l'état quand l'écran est tourné?

j'ai eu quelques difficultés à obtenir des sous-classes de référence de dialogue personnalisées à l'intérieur d'un fragment de préférence pour rester visible lorsque l'écran est tourné. Je n'éprouve pas ce problème en utilisant une Activitépréférence, donc je ne sais pas si c'est un bug Android ou un problème avec mon code, mais j'aimerais que quelqu'un me confirme s'il a la même expérience.

pour tester ceci, créez d'abord un écran de préférences contenant au moins un référentiel de dialogue (cela n'a pas d'importance ce qui sous-classe). Puis l'afficher dans une Activitépréférence. Lorsque vous lancez votre application, appuyez sur la boîte de dialogue Preference pour qu'elle s'affiche. Ensuite, faites tourner l'écran de façon à ce que l'orientation change. La boîte de dialogue restent visibles?

alors essayez la même chose, mais avec un Fragmentpréférence pour afficher vos préférences au lieu d'une Activitépréférence. Encore une fois, le dialogue restent visibles lorsque vous faites pivoter l'écran?

Jusqu'à présent, j'ai trouvé que le dialogue restera visible si on utilise un PreferenceActivity, mais pas si vous utilisez un PreferenceFragment. En regardant l' code source de DialogPreference, il semble que le bon comportement de la boîte de dialogue de rester visible, parce que isDialogShowing est l'état de l'information qui est enregistrée lorsque onSaveInstanceState() est appelé en Redirection à l'écran. Par conséquent, je pense qu'un bogue peut empêcher le Fragmentpréférence (et tout ce qui s'y trouve) de restaurer cette information d'état.

si C'est un bug Androïde, alors il a les implications de grande portée, parce que quiconque utilise Préférencefragment ne peut pas sauvegarder et restaurer l'information d'état.

quelqu'un Peut confirmer svp? Si ce n'est pas un bug, ce qui se passe?

27
demandé sur XåpplI'-I0llwlg'I - 2013-01-06 20:32:13

2 réponses

Enfin trouvé une solution à ce problème. Il s'avère que ce n'est pas un bug, mais un problème/oubli dans la documentation du développeur Android.

vous voyez, je suivais le tutoriel PreferenceFragment ici. Cet article vous dit de faire ce qui suit afin d'instancier votre fragment de préférence au sein d'une activité:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
} 

Le problème c'est que lorsque vous changez l'orientation de l'écran (ou toute autre action qui détruit et re-creates the Activity), votre fragment de préférence sera créé deux fois, ce qui lui fait perdre son état.

premier la création se fera via l'appel de L'activité à super.onCreate() (montré ci-dessus), qui appellera le onActivityCreated() méthode pour votre PreferenceFragment () et onRestoreInstanceState() méthode pour chaque Préférence qu'il contient. Ces succès de restaurer l'état de tout.

Mais une fois que l'appel à super.onCreate() retourne, vous pouvez voir que le onCreate() méthode à créer le PreferenceFragment deuxième fuseau. Parce qu'il est inutilement créé à nouveau (et cette fois, sans information de l'état!), tout l'état qui vient d'être restauré avec succès sera complètement abandonné/perdu. Cela explique pourquoi une référence de dialogue qui peut montrer au moment où L'activité est détruite ne sera plus visible une fois que l'activité est recréée.

alors quelle est la solution? Eh bien, il suffit d'ajouter un petit check pour déterminer si le fragment PreferenceFragment a déjà été créé, comme ceci:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content);
        if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class))
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

Ou une autre façon est de vérifier simplement si onCreate() est destiné à restaurer l'état ou pas, comme suit:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null)
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

alors je suppose que la leçon apprise ici est que onCreate() a un double rôle: il peut mettre en place une Activité pour la première fois, ou il peut restaurer à partir d'un état antérieur.

La réponse ici m'a amené à réaliser ceci solution.

47
répondu XåpplI'-I0llwlg'I - 2017-05-23 12:17:35

j'ai moi-même eu ce problème. Il y a un bug où DialogFragment ne restaure pas l'état parce que c'est nul, ou du moins ça m'est arrivé.

en utilisant plusieurs sources, j'ai finalement trouvé une solution qui fonctionne. Votre dialogue étendre cette BaseDialogFragment:

import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.app.DialogFragment;

import com.actionbarsherlock.app.SherlockDialogFragment;

public class BaseDialogFragment extends DialogFragment {

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        setRetainInstance(true);
        Log.d("TAG", "saved instance state oncreate: "
                + WorkaroundSavedState.savedInstanceState);
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;
        Log.d("TAG", "saved instance state oncretaedialog: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateDialog(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        Log.d("TAG", "saved instance state oncretaeview: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDestroyView() // necessary for restoring the dialog
    {
        if (getDialog() != null && getRetainInstance())
            getDialog().setOnDismissListener(null);

        super.onDestroyView();
    }

    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        // ...

        super.onSaveInstanceState(outState);
        WorkaroundSavedState.savedInstanceState = outState;
        Log.d("TAG", "saved instance state onsaveins: "
                + WorkaroundSavedState.savedInstanceState);

    }

    @Override
    public void onDestroy()
    {
        WorkaroundSavedState.savedInstanceState = null;
        super.onDestroy();
    }

    /**
     * Static class that stores the state of the task across orientation
     * changes. There is a bug in the compatibility library, at least as of the
     * 4th revision, that causes the save state to be null in the dialog's
     * onRestoreInstanceState.
     */
    public static final class WorkaroundSavedState {
        public static Bundle savedInstanceState;
    }
}

Notez que dans toutes les sous-classes dont les méthodes ont un savedInstanceState paramètre, vous pouvez avoir à faire appel super WorkaroundSavedState.savedInstanceState. Et quand vous restaurez l'état (i.e. dans onCreate(), ignorez juste le savedInstanceState et au lieu d'utiliser WorkaroundSavedState.savedInstanceState. Le support statique n'est pas la solution la plus propre, mais ça marche. Assurez-vous juste de le définir à null dans votre onDestroy().

dans tous les cas, mon DialogFragment ne disparaissent pas lorsque je tourne l'écran (et c'est sans aucune configChanges). Faites-moi savoir si ce code traite de votre problème et si non je vais jeter un oeil à ce qui se passe. Notez aussi que je ne l'ai pas testé dans PreferenceFragment mais à la place autre Fragment s de la classe de compatibilité ou de ActionBarSherlock.

0
répondu Oleg Vaskevich 2013-01-11 22:40:55