36

Problem
obracania urządzenia z portretem jednego okienka PreferenceScreen do dwóch tafli krajobrazu PreferenceScreen powoduje krajobraz pokazać tylko jednej szyby. NIE występuje podczas przeglądania ekranu nagłówków.Problemy z ekranami uprzywilejowanych dual-szybowych

Konfiguracja
Jest to ustawienie dla ICS i tylko dla up. Mam PreferenceActivity, który ładuje preference-headers. Każdy nagłówek łączy się z Fragment, który z kolei ładuje PreferenceScreen. Ładny bieg mil.

Szczegóły
Wszystko działało dobrze, dopóki nie zauważył, że Android będzie tylko auto-switch do spojrzenia na dwa okienka dla niektórych ekranach. Po pewnych badaniach dowiedziałem się od Commonsware post, że Android zrobi to tylko dla sw720dp. Trochę marnotrawstwa, jeśli zapytasz mnie, ponieważ wiele urządzeń def ma dużo miejsca na dwie tafle. Więc zastąpiłem metodę onIsMultiPane(), aby zwrócić wartość true dla w600dp i wyższą. Pracował jak urok .... trochę.

Biorąc pod uwagę urządzenie, które pokaże pojedyncze okienko w układzie pionowym i podwójnym w poziomie; oglądanie nagłówków w pionie i obracanie do krajobrazu, działa dobrze. Jeśli jednak wybierzesz nagłówek i załadujesz kolejny ekran w trybie pionowym, a następnie obrócisz go na poziomą, urządzenie pozostanie pojedynczym panelem zamiast przełączać z powrotem do podwójnego panelu. Jeśli wrócisz do ekranu nagłówków, powróci on do wyglądu z dwoma panelami, z tym, że nie zaznaczy nagłówka. W wyniku tego szczegółowy panel pozostaje pusty.

Czy to zamierzone zachowanie? W każdym razie obejść to? Próbowałem też przesłonić onIsHidingHeaders(), ale to spowodowało, że wszystko pokazało pusty ekran.

Kod
Preferencje aktywny:

public class SettingsActivity extends PreferenceActivity { 
@Override 
public void onBuildHeaders(List<Header> target) { 
    super.onBuildHeaders(target); 
    loadHeadersFromResource(R.xml.preference, target); 
} 

@Override 
public boolean onIsMultiPane() { 
    return getResources().getBoolean(R.bool.pref_prefer_dual_pane); 
} 
} 


preferencją Header Frag:

public class ExpansionsFragment extends PreferenceFragment { 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    addPreferencesFromResource(R.xml.pref_expansions); 
} 

public static ExpansionsFragment newInstance() { 
    ExpansionsFragment frag = new ExpansionsFragment(); 

    return frag; 
} 
} 
+0

Co "mówią" perspektywę debugowania? –

+0

Masz wartości, które zawierają stan "widoczny". –

+0

Funkcja PreferenceActivity obsługuje zmiany widoczności. W szczególności nie zmieniam tego. Powyższy kod jest dosłownie wszystkim, co robię. –

Odpowiedz

2

problem rozwiązany
Z jak popularne to pytanie stało się, postanowiłem ponownie powrócić do tego problemu ponownie i zobaczyć i f Mogę znaleźć rozwiązanie ... i zrobiłem to.Znaleziono małą, przyjemną pracę, która rozwiązuje pojedyncze okienko zamiast podwójnego panelu i zapewnia, że ​​nagłówek jest zawsze wybierany wstępnie w trybie podwójnego panelu.

Jeśli nie interesuje Cię wyjaśnienie, możesz po prostu przejść do kodu. Jeśli nie zależy Ci na systemie ICS, wiele kodu śledzenia nagłówka może zostać usuniętych, ponieważ JB dodał moduł pobierający dla listy tablic nagłówków.

Podwójny Pane Issue
Podczas przeglądania listy preferencji nagłówka w trybie pojedynczym lub podwójnym trybie tafli szyby, istnieje zawsze tylko jeden PreferenceActivity stworzony i to samo działanie dla obu przypadków. W rezultacie nigdy nie ma problemu z obsługą obracania ekranu, który przełącza tryb panela.

Jednak w trybie pojedynczego panelu po kliknięciu nagłówka odpowiedni fragment jest dołączony do NOWEJ PreferencjiAktywności. Ten nowy fragment zawierający PreferenceActivity nigdy nie wywołuje onBuildHeaders(). I dlaczego miałby to robić? Nie trzeba ich wyświetlać. To leży w tym problemie.

Po obróceniu tego fragmentu do trybu z dwoma panelami, nie ma żadnej listy nagłówków do pokazania, więc po prostu nadal wyświetla tylko ten fragment. Nawet jeśli wyświetliłby listę nagłówków, będziesz mieć problemy z backstackiem, ponieważ będziesz miał teraz dwie kopie nagłówków pokazujących PreferenceActivity. Kontynuuj klikanie wystarczającej liczby nagłówków, a będziesz mieć dość długi stos działań, aby użytkownik mógł nawigować z powrotem. W rezultacie odpowiedź jest prosta. Po prostu finish() aktywność. Następnie załaduje oryginalną PreferenceActivity, która ma listę nagłówków i odpowiednio wyświetli tryb podwójnego panelu.

Auto Wybór Header
Kolejna kwestia, która była potrzebna przeciwdziałanie że przełączanie między trybem single do podwójnej szyby z nową poprawką nie auto wybrać nagłówek. Została ci lista nagłówków i nie załadowano fragmentów szczegółów. Ta poprawka nie jest tak prosta. Zasadniczo wystarczy śledzić, który nagłówek został ostatnio kliknięty i zapewnić podczas tworzenia PreferenceActivity ... zawsze wybierany jest nagłówek.

To kończy się nieco irytujące w ICS, ponieważ API nie wystawia gettera na wewnętrznie śledzoną listę nagłówków. Android już utrzymuje tę listę i możesz ją technicznie odzyskać za pomocą tego samego, prywatnego, przechowywanego klucza, jednak jest to po prostu zły wybór. Zamiast tego proponuję ręcznie samodzielnie go utrwalić.

Jeśli nie zależy Ci na systemie ICS, możesz po prostu użyć metody getHeaders() ujawnionej w JB i nie martw się o żaden z zapisanych/przywróconych stanów.

Kod

public class SettingsActivity extends PreferenceActivity { 
private static final String STATE_CUR_HEADER_POS = "Current Position"; 
private static final String STATE_HEADERS_LIST = "Headers List"; 

private int mCurPos = AdapterView.INVALID_POSITION; //Manually track selected header position for dual pane mode 
private ArrayList<Header> mHeaders; //Manually track headers so we can select one. Required to support ICS. Otherwise JB exposes a getter instead. 

@Override 
public void onBuildHeaders(List<Header> target) { 
    loadHeadersFromResource(R.xml.preference, target); 
    mHeaders = (ArrayList<Header>) target; //Grab a ref of the headers list 
} 

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

    //This is the only code required for ensuring a dual pane mode shows after rotation of a single paned preference screen 
    if (onIsMultiPane() && onIsHidingHeaders()) { 
     finish(); 
    } 
} 

@Override 
public boolean onIsMultiPane() { 
    //Override this if you want dual pane to show up on smaller screens 
    return getResources().getBoolean(R.bool.pref_prefer_dual_pane); 
} 

@Override 
protected void onListItemClick(ListView l, View v, int position, long id) { 
    super.onListItemClick(l, v, position, id); 

    //Intercept a header click event to record its position. 
    mCurPos = position; 
} 

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

    //Retrieve our saved header list and last clicked position and ensure we switch to the proper header. 
    mHeaders = state.getParcelableArrayList(STATE_HEADERS_LIST); 
    mCurPos = state.getInt(STATE_CUR_HEADER_POS); 
    if (mHeaders != null) { 
     if (mCurPos != AdapterView.INVALID_POSITION) { 
      switchToHeader(mHeaders.get(mCurPos)); 
     } else { 
      switchToHeader(onGetInitialHeader()); 
     } 
    } 
} 

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

    //Persist our list and last clicked position 
    if (mHeaders != null && mHeaders.size() > 0) { 
     outState.putInt(STATE_CUR_HEADER_POS, mCurPos); 
     outState.putParcelableArrayList(STATE_HEADERS_LIST, mHeaders); 
    } 
} 
} 
+0

To jest dobre rozwiązanie, ale wciąż ma jeden problem, który znalazłem. Przypuśćmy, że dla uproszczenia wielopanelowego jest prawdziwe, że jesteś w trybie poziomym. Teraz uruchom aplikację w trybie pionowym (pojedyncze okienko), wybierz nagłówek, przełącz na poziomy (wielopanelowy), a następnie przejdź do trybu portretu. Problem polega na tym, że wybrany przedmiot nie jest już przywracany. Właśnie wrócił do głównego ekranu ustawień, pokazując wszystkie nagłówki. Ale to nie jest duży problem. Po prostu dziwne... –

0

Kluczową ideą kodu poniżej pochodzi z wpisu na blogu Commonsware połączonego w pytanie, więc wydaje się istotne. Musiałem w szczególności rozwinąć tę koncepcję, aby poradzić sobie z problemem zmiany orientacji, który brzmi bardzo podobnie do tego, o którym mowa, więc mam nadzieję, że to początek.

Klasy ustawień nie powinny mieć żadnego wpływu na problem z orientacją, ale uwzględnienie jej i tak jest jasne.

Za mój kod komentarza, sprawdzić, czy połączenie w onCreatecheckNeedsResource pomoże w ogóle:

public class SettingsActivity 
extends 
    PreferenceActivity 
{ 

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

    // Show settings without headers for single pane or pre-Honeycomb. Make sure to check the 
    // single pane or pre-Honeycomb condition again after orientation change. 
    if (checkNeedsResource()) { 
     MyApp app = (MyApp)getApplication(); 
     SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app); 
     Settings settings = new Settings(); 
     addPreferencesFromResource(R.xml.prefs_api); 
     settings.setupPreference(findPreference(MyApp.KEY_USERNAME), prefs.getString(MyApp.KEY_USERNAME, null), true); 
     settings.setupPreference(findPreference(MyApp.KEY_API_URL_ROOT), prefs.getString(MyApp.KEY_API_URL_ROOT, null), true); 
     if (this.isHoneycomb) { 
      // Do not delete this. We may yet have settings that only apply to Honeycomb or higher. 
      //addPreferencesFromResource(R.xml.prefs_general); 
     } 
     addPreferencesFromResource(R.xml.prefs_about); 
     settings.setupPreference(findPreference(MyApp.KEY_VERSION_NAME), app.getVersionName()); 
    } 
} 

@TargetApi(Build.VERSION_CODES.HONEYCOMB) 
@Override 
public void onBuildHeaders(List<Header> target) { 
    super.onBuildHeaders(target); 

    // This check will enable showing settings without headers for single pane or pre-Honeycomb. 
    if (!checkNeedsResource()) { 
     loadHeadersFromResource(R.xml.pref_headers, target); 
    } 
} 

private boolean checkNeedsResource() { 
    // This check will enable showing settings without headers for single pane or pre-Honeycomb. 
    return (!this.isHoneycomb || onIsHidingHeaders() || !onIsMultiPane()); 
} 

private boolean isHoneycomb = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB); 

}

public class Settings { 

public Settings() { 
} 

public void setupPreference(Preference pref, String summary, boolean setChangeListener) { 
    if (pref != null) { 
     if (summary != null) { 
      pref.setSummary(summary); 
     } 

     pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 

      @Override 
      public boolean onPreferenceChange(Preference pref, Object newValue) { 
       pref.setSummary(newValue.toString()); 
       return true; 
      } 

     }); 
    } 
} 

public void setupPreference(Preference pref, String summary) { 
    setupPreference(pref, summary, false); 
} 

}

+0

Nie jestem do końca pewien, co tu zamieściłeś, ale to nie działa. W rzeczywistości nie jestem pewien, w jaki sposób radzi sobie z każdą zmianą orientacji. Nie martwię się też o nic przed ICS, więc żadne z tych wycofanych działań addPreferencesFromResource nie ma związku z moim pytaniem ... ani twoja preferencja nie została zmieniona. –

+0

Przyczyną, dla której rozwiązano problem, na który się natknąłem, jest zaznaczenie opcji onCreate, która występuje po zmianie orientacji. To, że nie pomogło w sprawie, na którą się natknąłeś, było mniej lub bardziej ukryte w moim "mam nadzieję ...". Cieszę się, że go trzymałeś i opublikowałeś swoją rezolucję. –