Ответ 1
Проблема решена
Насколько популярным стал этот вопрос, я решил снова вернуться к этой проблеме и посмотреть, смогу ли найти разрешение... и я это сделал. Нашел неплохую работу, которая разрешает показ одиночной панели вместо двойной панели и гарантирует, что заголовок всегда будет предварительно выбран в режиме двойной панели.
Если вам неинтересно объяснение, вы можете просто перейти к коду. Если вам неинтересно ICS, многие из кода отслеживания заголовков могут быть удалены, так как JB добавил геттер для списка массивов заголовков.
Проблема с двойной панелью
При просмотре списка заголовков предпочтений в режиме одиночной панели или двойного окна создается только одна функция PreferenceActivity, и для обоих случаев она одинаковая. В результате никогда не возникает проблем при обработке поворотов экрана, которые переключают режим панели.
Однако в режиме одиночной панели, когда вы нажимаете на заголовок, соответствующий фрагмент присоединяется к NEW PreferenceActivity. Этот новый фрагмент, содержащий PreferenceActivity, никогда не вызывает onBuildHeaders()
. И почему? Его не нужно отображать. В этом проблема.
При вращении этого фрагмента в режим двойной панели он не имеет никакого списка заголовков, чтобы показать, что он просто продолжает показывать только фрагмент. Даже если он показывает список заголовков, у вас будут проблемы с backstack, так как теперь у вас будет две копии заголовков PreferenceActivity. Продолжайте нажимать достаточно заголовков, и вы получите довольно большой стек действий, чтобы пользователь мог вернуться назад. В результате ответ прост. Просто finish()
активность. Затем он загрузит исходную PreferenceActivity, которая имеет список заголовков и будет правильно отображать режим двойной панели.
Автоматический выбор заголовка
Следующий вопрос, требующий решения, заключался в том, что переключение между режимами с одиночной и двойной панелью с новым исправлением не автоматически выбирает заголовок. У вас остался список заголовков и загружен фрагмент деталей. Это исправление не так просто. В основном вам просто нужно следить за тем, какой последний заголовок был последним нажат, и обеспечить во время создания PreferenceActivity... заголовок всегда выбирается.
Это заканчивается тем, что немного раздражает в ICS, поскольку API не раскрывает геттер для списка отслеживаемых внутренними гусеницами. Android уже сохраняет этот список, и вы можете технически получить его, используя тот же закрытый встроенный строковый ключ, однако это просто плохой выбор дизайна. Вместо этого я предлагаю вручную снова сохранить его снова.
Если вам неинтересно ICS, вы можете просто использовать метод getHeaders()
, открытый в JB, и не беспокоиться об этом состоянии состояния сохраненного/восстановленного.
Код
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);
}
}
}