26

Ich hatte einige Probleme, einige benutzerdefinierte DialogPreference-Unterklassen in einem PreferenceFragment zu erhalten, um sichtbar zu bleiben, wenn der Bildschirm gedreht wird. Ich habe dieses Problem nicht, wenn ich eine PreferenceActivity verwende, daher weiß ich nicht, ob es sich um einen Android-Fehler oder ein Problem mit meinem Code handelt, aber ich möchte, dass jemand bestätigt, ob er die gleiche Erfahrung macht.Warum wird Fragment nicht beibehalten, wenn der Bildschirm gedreht wird?

Um dies zu testen, erstellen Sie zunächst ein Einstellungsfenster mit mindestens einer DialogPreference (egal welche Unterklasse). Zeigen Sie es dann in einer PreferenceActivity an. Wenn Sie Ihre App ausführen, drücken Sie auf die DialogPreference, so dass der Dialog angezeigt wird. Drehen Sie dann den Bildschirm, so dass sich die Ausrichtung ändert. Bleibt der Dialog sichtbar?

Dann versuchen Sie das gleiche, aber mit einem PreferenceFragment, um Ihre Einstellungen anstelle einer PreferenceActivity anzuzeigen. Ist der Dialog weiterhin sichtbar, wenn Sie den Bildschirm drehen?

Bisher habe ich festgestellt, dass der Dialog sichtbar bleibt, wenn eine PreferenceActivity verwendet wird, aber nicht, wenn PreferenceFragment verwendet wird. Betrachtet man die source code for DialogPreference, so scheint es, dass das korrekte Verhalten darin besteht, dass der Dialog sichtbar bleibt, weil isDialogShowing die Statusinformation ist, die gespeichert wird, wenn onSaveInstanceState() auf der Bildschirmumorientierung aufgerufen wird. Daher denke ich, dass ein Fehler das PreferenceFragment (und alles darin) daran hindert, diese Statusinformationen wiederherzustellen.

Wenn es sich um einen Android-Fehler handelt, hat dies weitreichende Auswirkungen, da jeder, der PreferenceFragment verwendet, Statusinformationen nicht speichern und wiederherstellen kann.

Kann jemand bitte bestätigen? Wenn es kein Fehler ist, was passiert dann?

Antwort

45

Endlich eine Lösung für diese pr gefunden Problem. Es stellt sich heraus, dass es sich nicht um einen Fehler, sondern um ein Problem in der Android-Entwicklerdokumentation handelt.

Sie sehen, ich folgte dem PreferenceFragment Tutorial here.In diesem Artikel erfahren Sie, die folgende, um zu instanziiert Ihre PreferenceFragment innerhalb einer Aktivität zu tun:

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

Das Problem dabei ist, dass, wenn Sie die Bildschirmausrichtung ändern (oder jede andere Aktion, die zerstört und neu erstellt die Aktivität), wird Ihr PreferenceFragment zweimal erstellt, was dazu führt, dass es seinen Status verliert.

Die erste Schöpfung wird über den Aufruf der Aktivität auftreten zu super.onCreate() (siehe oben), die die onActivityCreated() Methode für Ihre PreferenceFragment wird() aufrufen und die onRestoreInstanceState() Methode für jede Präferenz enthält. Diese werden den Zustand von allem erfolgreich wiederherstellen.

Aber dann einmal diesen Anruf zu super.onCreate() zurückkehrt, können Sie sehen, dass die onCreate() Methode wird dann erstellen gehen auf der PreferenceFragment eine zweite Zeit. Da es sinnlos wieder erstellt wird (und dieses Mal ohne Statusinformationen!), Wird der gesamte Zustand, der gerade erfolgreich wiederhergestellt wurde, vollständig verworfen/verloren. Dies erklärt, warum eine DialogPreference, die zu dem Zeitpunkt angezeigt wird, zu dem die Aktivität zerstört wird, nicht mehr sichtbar ist, nachdem die Aktivität neu erstellt wurde.

Also, was ist die Lösung? Nun, fügen Sie einfach eine kleine Überprüfung, ob die PreferenceFragment bereits erstellt wurde, etwa so:

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

Oder ein anderer Weg ist, einfach zu überprüfen, ob onCreate() gemeint Zustand wiederherzustellen oder nicht, etwa so:

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

Also die Lektion hier gelernt ist, dass onCreate() eine Doppelrolle hat - es kann eine Aktivität zum ersten Mal einrichten, oder es kann aus einem früheren Zustand wiederherstellen.

Die Antwort here führte mich zur Realisierung dieser Lösung.

+0

Die dritte Option ist die Standardmethode zur Überprüfung der "ersten" onCreate Ausführung – SeanPONeil

+0

Wie würden Sie das tun? –

+1

if (savedInstanceState == null) {} Diese Anweisung wird nur bei der ersten Erstellung einer Aktivität auf "true" gesetzt – SeanPONeil

0

Ich hatte dieses Problem tatsächlich selbst. Es gibt einen Fehler, bei dem der DialogFragment den Status nicht wiederherstellt, weil er null ist, oder zumindest ist es mir passiert.

Mit mehreren Quellen habe ich schließlich eine Lösung arbeiten. Haben Sie Ihren Dialog diese erweitern 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; 
    } 
} 

Beachten Sie, dass in allen Unterklassen, deren Methoden haben einen savedInstanceState Parameter kann man super mit WorkaroundSavedState.savedInstanceState verlangen. Und wenn Sie Zustand sind die Wiederherstellung (dh in onCreate(), ignorieren einfach die savedInstanceState und stattdessen WorkaroundSavedState.savedInstanceState verwenden. Der statische Halter ist nicht die sauberste Lösung, aber es funktioniert. So stellen Sie sicher, dass es in Ihrem onDestroy() auf null zu setzen.

In jedem Fall verschwindet meine DialogFragment nicht, wenn ich den Bildschirm rotiere (und das ist ohne configChanges). Lassen Sie mich wissen, wenn dieser Code Ihr Problem anspricht und wenn nicht, werde ich einen Blick auf das werfen, was vor sich geht habe dies nicht innerhalb PreferenceFragment getestet, sondern stattdessen andere Fragment s aus der Kompatibilitätsklasse oder ActionBarSherlock.

+0

Warum eine Abhängigkeit von Drittanbietern (ActionBarSherlock) einführen? –

+0

Sie müssen nicht. Lassen Sie es 'DialogFragment' stattdessen erweitern –

+0

Ich redigierte gerade meine Antwort. Persönlich habe ich 'SherlockDialogFragment' nur benutzt, weil ich ABS benutzt habe, aber meine Antwort sollte auch ohne ABS funktionieren. Probieren Sie es aus, indem Sie Ihre eigenen 'DialogFragment's diese Klasse erweitern. –