36

Problema
Rotazione un dispositivo da un ritratto di un riquadro PreferenceScreen ad un paesaggio due pannelli PreferenceScreen, provoca paesaggio per mostrare solo come un riquadro. NON si verifica quando si visualizza la schermata delle intestazioni.Problemi con doppio riquadro schermi privilegiate

Impostazione
Questo è solo per ICS e solo. Ho un PreferenceActivity che carica preference-headers. Ogni intestazione collega con un Fragment, che a sua volta carica un PreferenceScreen. Bella corsa di mil.

dettagli
Tutto ha funzionato bene fino a quando ho notato che Android sarà solo auto-switch ad uno sguardo a due pannelli per alcune schermate. Dopo alcune ricerche ho appreso da uno Commonsware post che Android lo farà solo per sw720dp. Un po 'di spreco se me lo chiedi dato che molti dispositivi def hanno molto spazio per due riquadri. Quindi ho eseguito l'override del metodo onIsMultiPane() per restituire true per w600dp e versioni successive. Ha funzionato come un fascino .... kinda.

Dato un dispositivo che mostra un riquadro singolo in verticale e doppio riquadro in orizzontale; visualizzare le intestazioni in verticale e ruotarle in orizzontale, funziona bene. Tuttavia, se si seleziona un'intestazione e si carica la schermata successiva in modalità verticale, ruotare in orizzontale per far sì che il dispositivo rimanga un riquadro singolo anziché tornare al doppio pannello. Se torni indietro alla schermata delle intestazioni, tornerà a un doppio riquadro, tranne che non pre-selezionerà un'intestazione. Di conseguenza il riquadro dettagliato rimane vuoto.

È questo comportamento previsto? Ad ogni modo per aggirarlo? Ho provato a sovrascrivere anche lo onIsHidingHeaders() ma questo ha solo causato il tutto per mostrare una schermata vuota.

Codice
Preferenze attività:

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


una preferenza intestazione 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

cosa "dire" una prospettiva di debug? –

+0

Hai un valore che contiene lo stato "visibile" intendo. –

+0

PreferenceActivity gestisce i cambiamenti di visibilità. In particolare, non sto alterando nulla di tutto ciò. Il codice sopra è letteralmente tutto ciò che sto facendo. –

risposta

2

Problema risolto
Con quanto popolare questa domanda è diventata, ho deciso di rivisitare nuovamente questo problema e vedere i f Sono riuscito a trovare una soluzione ... e l'ho fatto.Trovato un bel po 'di lavoro in giro che risolve il singolo riquadro che mostra invece di doppio riquadro e garantendo un'intestazione è sempre pre-selezionata quando in modalità doppio pannello.

Se non ti interessa una spiegazione, puoi semplicemente saltare al codice. Se non ti interessa di ICS, un sacco del codice di tracciamento dell'intestazione può essere rimosso poiché JB ha aggiunto un getter per l'elenco di matrice di intestazioni.

a doppio pannello Problema
Quando si visualizza l'elenco di intestazione preferenza in modalità riquadro singolo o in modalità a doppio pannello, v'è sempre e solo uno PreferenceActivity creato ed è la stessa attività per entrambi i casi. Di conseguenza, non c'è mai un problema nella gestione delle rotazioni dello schermo che cambierà la modalità del riquadro.

Tuttavia, in modalità riquadro singolo quando si fa clic su un'intestazione, il frammento corrispondente è collegato a una NUOVA preferenza di preferenza. Questo nuovo frammento contenente PreferenceActivity non invoca mai onBuildHeaders(). E perché dovrebbe? Non ha bisogno di visualizzarli. Questa bugia è il problema.

Quando si ruota quel frammento in una modalità a doppio riquadro, non ha alcun elenco di intestazioni da mostrare in modo che continui a mostrare solo il frammento. Anche se mostrava l'elenco delle intestazioni, avrai dei problemi con i backstacks poiché ora avresti due copie di PreferenceActivity che mostrano le intestazioni. Continua a fare clic su abbastanza intestazioni e avrai una serie di attività abbastanza lunghe da consentire all'utente di tornare indietro. Di conseguenza, la risposta è semplice. Solo finish() l'attività. Verrà quindi caricata la PreferenceActivity originale che DEVE avere l'elenco di intestazione e mostrerà correttamente la modalità doppio riquadro.

Auto Selezione Header
Il prossimo numero che doveva affrontare era che il passaggio tra singolo alla modalità a doppio pannello con la nuova correzione non ha fatto selezione automatica un colpo di testa. Sei rimasto con un elenco di intestazioni e nessun frammento di dettagli caricato. Questa correzione non è così semplice. Fondamentalmente devi solo tenere traccia di quale intestazione è stata fatta l'ultima volta e assicurarti durante la creazione di PreferenceActivity ... un'intestazione è sempre selezionata.

Questo risulta un po 'fastidioso in ICS poiché l'API non espone un getter per l'elenco di intestazioni tracciate internamente. Android mantiene già tale elenco e potresti recuperarlo tecnicamente utilizzando la stessa chiave stringa interna archiviata privatamente, tuttavia si tratta solo di una cattiva scelta progettuale. Invece, ti suggerisco di riprenderlo manualmente da solo.

Se non ti interessa di ICS, puoi semplicemente utilizzare il metodo getHeaders() esposto in JB e non preoccuparti di nessuno di questi elementi salvati/ripristinati.

Codice

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

Questa è una buona soluzione, ma ha ancora un problema che ho trovato. Supponiamo per semplicità il multi-riquadro è vero se si è in modalità orizzontale. Ora esegui l'app in modalità verticale (riquadro singolo), scegli un'intestazione, passa a orizzontale (a più riquadri) e passa alla modalità verticale. Il problema è che l'elemento selezionato non viene più ripristinato. È appena tornato alla schermata delle impostazioni principali, mostrando tutte le intestazioni. Non è un grosso problema però. Strano ... –

0

L'idea chiave dietro il codice qui sotto è venuto dalla voce del blog Commonsware collegato in la domanda, quindi sembra pertinente. In particolare, ho dovuto estendere il concetto per affrontare un problema di modifica dell'orientamento che sembra molto simile a quello della domanda, quindi spero che vi dia un inizio.

La classe Impostazioni non dovrebbe incidere sul problema dell'orientamento, ma includerlo comunque per essere chiari.

Per il mio commento codice, vedere se la chiamata checkNeedsResource in onCreate aiuterà a tutti:

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

Non sono esattamente sicuro di quello che hai postato qui, ma questo non funziona. In effetti, non sono sicuro di come questo possa gestire eventuali cambiamenti di orientamento. Non sono nemmeno preoccupato di nulla prima di ICS, quindi nessuno di quel deprecato comportamento di addPreferencesFromResource è rilevante per la mia domanda ... né la tua preferenza è cambiata listener. –

+0

Il motivo per cui è stato risolto il problema che stavo eseguendo è il check-in onCreate, che si verifica dopo un cambio di orientamento. Il fatto che non sia stato d'aiuto con il problema che stavi incontrando era più o meno coperto nel mio "ecco sperando ...". Sono contento che tu l'abbia seguito e pubblicato la tua risoluzione. –

Problemi correlati