Problème résolu
Avec la popularité de cette question, j'ai décidé de revenir sur ce problème et de voir si je pouvais trouver une solution... et je l'ai fait. J'ai trouvé un petit contournement qui résout le problème de l'affichage d'un seul panneau au lieu de deux et qui garantit qu'un en-tête est toujours présélectionné en mode double panneau.
Si vous ne voulez pas d'explication, vous pouvez passer directement au code. Si vous ne vous souciez pas d'ICS, une grande partie du code de suivi des en-têtes peut être supprimée car JB a ajouté un getter pour la liste du tableau des en-têtes.
Problème de double vitrage
Lors de l'affichage de la liste d'en-tête des préférences en mode volet unique ou en mode double volet, une seule PreferenceActivity est créée et il s'agit de la même activité dans les deux cas. Par conséquent, il n'y a jamais de problème pour gérer les rotations d'écran qui changent le mode de volet.
Cependant, en mode volet unique, lorsque vous cliquez sur un en-tête, le fragment correspondant est attaché à une NOUVELLE PreferenceActivity. Ce nouveau fragment contenant PreferenceActivity n'invoque jamais onBuildHeaders()
. Et pourquoi le ferait-il ? Il n'a pas besoin de les afficher. Ce mensonge est le problème.
Lorsque l'on fait pivoter ce fragment en mode double volet, il n'y a pas de liste d'en-tête à afficher et il continue donc à n'afficher que le fragment. Même si elle affichait la liste des en-têtes, vous auriez des problèmes de backstack car vous auriez maintenant deux copies de la PreferenceActivity affichant les en-têtes. Si vous cliquez sur un nombre suffisant d'en-têtes, vous obtiendrez une longue pile d'activités dans laquelle l'utilisateur devra naviguer. Par conséquent, la réponse est simple. Il suffit d'aller sur finish()
l'activité. Il chargera ensuite l'activité PreferenceActivity originale qui contient la liste d'en-tête et affichera correctement le mode double volet.
Sélection automatique de l'en-tête
Le problème suivant à résoudre était que le passage du mode simple au mode double volet avec le nouveau correctif ne permettait pas de sélectionner automatiquement un en-tête. Vous vous retrouviez avec une liste d'en-têtes et aucun fragment de détails chargé. Cette correction n'est pas aussi simple. Fondamentalement, vous devez juste garder la trace de l'en-tête qui a été cliqué en dernier et s'assurer que pendant la création de PreferenceActivity... un en-tête est toujours sélectionné.
Cela finit par être un peu ennuyeux dans ICS puisque l'API n'expose pas un getter pour la liste d'en-têtes suivie en interne. Android fait déjà persister cette liste et vous pourriez techniquement la récupérer en utilisant la même clé de chaîne interne stockée en privé, mais c'est un mauvais choix de conception. Au lieu de cela, je suggère de la faire persister manuellement vous-même.
Si vous ne vous souciez pas de l'ICS, alors vous pouvez simplement utiliser l'option getHeaders()
exposée dans JB et ne pas se préoccuper de cette histoire de sauvegarde/restauration d'état.
Code
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);
}
}
}