26

Mam pewne problemy z uzyskaniem niektórych niestandardowych podklas DialogPreference wewnątrz PreferenceFragment, aby pozostały widoczne po obróceniu ekranu. Nie widzę tego problemu podczas korzystania z PreferenceActivity, więc nie wiem, czy to błąd Androida, czy problem z moim kodem, ale chciałbym, aby ktoś potwierdził, czy ma to samo doświadczenie.Dlaczego fragment nie zachowuje stanu po obróceniu ekranu?

Aby to przetestować, najpierw utwórz ekran preferencji zawierający co najmniej jeden DialogPreference (nie ma znaczenia, która podklasa). Następnie wyświetl go w PreferenceActivity. Po uruchomieniu aplikacji naciśnij DialogPreference, aby wyświetlało się okno dialogowe. Następnie obróć ekran, aby zmienić orientację. Czy okno dialogowe pozostaje widoczne?

Następnie spróbuj tego samego, ale z PreferenceFragment, aby wyświetlić preferencje zamiast PreferenceActivity. Ponownie, czy okno dialogowe pozostaje widoczne po obróceniu ekranu?

Do tej pory odkryłem, że okno dialogowe pozostanie widoczne przy użyciu właściwości PreferenceActivity, ale nie w przypadku używania PreferenceFragment. Patrząc na source code for DialogPreference, wydaje się, że prawidłowe zachowanie jest widoczne, ponieważ okno dialogowe pozostaje widoczne, ponieważ isDialogShowing jest informacją o stanie, która jest zapisywana, gdy wywoływana jest re-orientacja ekranu, gdy onSaveInstanceState(). Dlatego myślę, że błąd może uniemożliwić PreferenceFragment (i wszystko w nim) z przywracania informacji o stanie.

Jeśli jest to błąd Androida, ma on daleko idące konsekwencje, ponieważ każdy korzystający z PreferenceFragment nie może zapisywać ani przywracać informacji o stanie.

Czy ktoś może potwierdzić? Jeśli to nie błąd, to co się dzieje?

Odpowiedz

45

W końcu znalazłem rozwiązanie tego pr oblem. Okazuje się, że to nie błąd, ale problem/niedopatrzenie w dokumentacji programisty Androida.

Widzisz, śledziłem samouczek PreferenceFragment here.Że artykuł mówi, aby wykonać następujące czynności w celu wystąpienia swoją PreferenceFragment ramach działalności:

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

Problem polega na tym, że po zmianie orientacji ekranu (lub jakiegokolwiek innego działania, które niszczy i ponownie tworzy aktywny) twój PreferenceFragment zostanie utworzony dwukrotnie, co powoduje, że traci on swój stan.

pierwszy tworzenie nastąpi poprzez wywołania takiej aktywności do super.onCreate() (powyżej), które będą wymagały metody onActivityCreated() dla PreferenceFragment (,) oraz metodę onRestoreInstanceState() każdego preferencji zawiera. Pomogą one z powodzeniem przywrócić stan wszystkiego.

Ale wtedy po tym wywołaniu super.onCreate() zwrotów, można zobaczyć, że metoda onCreate() będzie następnie przejść do utworzenia PreferenceFragment się drugą czasu. Ponieważ jest on bezsensownie tworzony ponownie (i tym razem bez informacji o stanie!), Cały stan, który został pomyślnie przywrócony, zostanie całkowicie odrzucony/utracony. To wyjaśnia, dlaczego DialogPreference, który może być wyświetlany w czasie niszczenia działania, nie będzie już widoczny po odtworzeniu działania.

Jakie jest rozwiązanie? Cóż, po prostu dodać niewielką kontrolę w celu ustalenia, czy PreferenceFragment został już utworzony, tak:

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

albo innym sposobem jest po prostu sprawdzić, czy onCreate() ma na celu przywrócenie stanu lub nie, jak tak:

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

Sądzę więc, że lekcja, o której tu mowa, polega na tym, że onCreate() ma podwójną rolę - może ustawić działanie po raz pierwszy lub przywrócić go z wcześniejszego stanu.

Odpowiedź here doprowadziła mnie do realizacji tego rozwiązania.

+0

Trzecia opcja to standardowy sposób sprawdzania, czy "pierwsze" zostało wykonane. – SeanPONeil

+0

Jak byś to zrobił? –

+1

if (savedInstanceState == null) {}, że instrukcja zostanie rozwiązana na true tylko przy pierwszym utworzeniu działania – SeanPONeil

0

Rzeczywiście miałem ten problem osobiście. Występuje błąd, w którym DialogFragment nie przywraca stanu, ponieważ jest pusty lub przynajmniej mi się to zdarzyło.

Korzystanie z wielu źródeł W końcu otrzymałem rozwiązanie działające. Wyraź swoją dialogowe przedłużyć ten 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; 
    } 
} 

Należy zauważyć, że w żadnej podklasy, których metody mają parametr savedInstanceState, być może trzeba będzie zadzwonić SUPER WorkaroundSavedState.savedInstanceState. A kiedy jesteś przywrócenie stanu (czyli w onCreate(), po prostu zignorować savedInstanceState i zamiast używać WorkaroundSavedState.savedInstanceState. Posiadacz statyczny nie jest najczystszym rozwiązanie, ale działa. Wystarczy upewnić się, aby ustawić go na wartość null w swojej onDestroy().

W każdym razie mój DialogFragment nie znika po obróceniu ekranu (i to bez żadnego configChanges). Daj mi znać, jeśli ten kod rozwiązuje Twój problem, a jeśli nie, przyjrzę się temu, co się dzieje. nie przetestowałem tego w ramach PreferenceFragment, ale zamiast tego inne Fragment s z klasy zgodności lub z ActionBarSherlock.

+0

Po co wprowadzać zależność od strony trzeciej (ActionBarSherlock)? –

+0

Nie musisz. Po prostu rozbuduj 'DialogFragment' zamiast: –

+0

Właśnie edytowałem swoją odpowiedź. Osobiście właśnie używałem 'SherlockDialogFragment', ponieważ używałem ABS, ale moja odpowiedź powinna działać również bez ABS. Wypróbuj teraz, korzystając z własnego '' DialogFragment's rozszerzenia tej klasy. –

Powiązane problemy