Проблемы с экранами предпочтений с двумя панелями

проблема
Поворот устройства из одноуровневого портрета PreferenceScreen на двухэлементном ландшафте PreferenceScreen приводит к тому, что пейзаж отображается только как одноэлемент. Не происходит при просмотре экрана заголовков.

Настроить
Это для ICS и только для. У меня есть PreferenceActivity которая загружает preference-headers . Каждый заголовок соединяется с Fragment , который, в свою очередь, загружает PreferenceScreen . Довольно бег mil.

Детали
Все работало хорошо, пока я не заметил, что Android только автоматически переключится на двухпанельный вид некоторых экранов. После некоторых исследований я узнал из сообщения Commonsware, что Android будет делать это только для sw720dp. Немного отходов, если вы спросите меня, так как многие устройства Def имеют достаточно места для двух панелей. Поэтому я переопределил метод onIsMultiPane() чтобы вернуть true для w600dp и выше. Работал как очарование …. любопытное.

Учитывая устройство, которое будет показывать однослойное изображение в портретном и двойном окне в ландшафте; Просмотр заголовков в портрете и поворот на пейзаж, прекрасно работает. Однако, если вы выбираете заголовок и загружаете его последующий экран в портретном режиме, а затем поворачивайте в альбомное положение, устройство будет оставаться на одной панели вместо переключения на двойную панель. Если вы затем перейдете к экрану заголовков, он вернется к двойному окну, за исключением того, что он не будет предварительно выбирать заголовок. В результате подробная панель остается пустой.

Это намеренное поведение? В любом случае, чтобы обойти это? Я попробовал переопределить onIsHidingHeaders() но это просто заставило все показать пустой экран.

Код
Предпочтение:

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

Заголовок предпочтения 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; } } 

    Задача решена
    Насколько популярным стал этот вопрос, я решил снова рассмотреть этот вопрос и посмотреть, смогу ли найти разрешение … и я это сделал. Нашел небольшую небольшую работу, которая разрешает отображение одиночной панели вместо двойной панели и гарантирует, что заголовок всегда будет предварительно выбран в режиме двойной панели.

    Если вас не интересует объяснение, вы можете просто перейти к коду. Если вам неинтересно ICS, многие из кода отслеживания заголовков могут быть удалены, так как JB добавил геттер для списка массивов заголовков.

    Проблема с двойной панелью
    При просмотре списка заголовков предпочтений в режиме одиночной панели или в режиме двойной панели создается только одна функция PreferenceActivity, и для обоих случаев это одно и то же действие. В результате никогда не возникает проблем при обработке поворотов экрана, которые переключают режим панели.

    Однако в режиме одиночной панели, когда вы нажимаете на заголовок, соответствующий фрагмент присоединяется к NEW PreferenceActivity. Этот новый фрагмент, содержащий PreferenceActivity, никогда не вызывает onBuildHeaders() . И почему? Его не нужно отображать. В этом проблема.

    При вращении этого фрагмента в режим двойной панели он не имеет никакого списка заголовков, чтобы показать, что он просто продолжает показывать только фрагмент. Даже если он отобразил список заголовков, у вас будут некоторые проблемы с backstack, так как теперь у вас будет две копии заголовков PreferenceActivity. Продолжайте нажимать достаточно заголовков, и вы получите довольно большой стек действий, чтобы пользователь мог вернуться назад. В результате ответ прост. Просто finish() действие. Затем он загрузит исходную PreferenceActivity, которая имеет список заголовков и будет правильно отображать режим двойной панели.

    Автоматический выбор заголовка
    Следующий вопрос, требующий решения, заключался в том, что переключение между режимами с одиночной и двойной панелью с новым исправлением не автоматически выбирает заголовок. У вас остался список заголовков и загружен фрагмент деталей. Это исправление не так просто. В основном вам просто нужно следить за тем, какой последний заголовок был последним нажат и обеспечить во время создания PreferenceActivity … заголовок всегда выбирается.

    Это заканчивается тем, что немного раздражает ICS, поскольку API не предоставляет геттер для списка отслеживаемых внутренними треками. Android уже сохраняет этот список, и вы можете технически получить его, используя тот же закрытый встроенный строковый ключ, но это всего лишь плохой выбор дизайна. Вместо этого я предлагаю вручную снова сохранить это снова.

    Если вам getHeaders() 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); } } } 

    Основная идея этого кода приведена в блоге Commonsware, связанном с вопросом, поэтому он считает, что это важно. Я специально должен был расширить концепцию, чтобы иметь дело с проблемой изменения ориентации, которая звучит очень похоже на вопрос в вопросе, поэтому мы надеемся, что это даст вам начало.

    Класс Settings не должен иметь никакого отношения к проблеме ориентации, но включая его в любом случае, чтобы быть ясным.

    В моем комментарии к коду, посмотрите, checkNeedsResource вызов onCreate в onCreate :

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

    }

    Intereting Posts
    Crash casting AndroidKeyStoreRSAPrivateKey для RSAPrivateKey Возможно отображать анимированное изображение gif в эмуляторе. Использование phoneGap Textview onclicklistener со ссылками Могу ли я проверить на своем сервере, что пользователь заплатил за приложение Android через Google Play Store? Восприятие сходства между двумя аудиопоследовательностями Как сохранить и получить путь к картинке Cropped Android с SD-карты? Как я могу позволить пользователю повторно сортировать элементы в списке? Android потребляемый: «уже принадлежит этот элемент», но inventory.hasPurchase () – false Как дать строку под Textview в android Почему String.format () не работает в Android TextView? Делегировать преобразование текста в «плагины» приложений для Android, не известных заранее Volley NoConnectionError: tlsv1 предупреждает о неподходящем возврате Android paddingBottom не работает без paddingTop Отправлять данные в приемник сигналов тревоги Изменение цвета progressbar через CODE ONLY на Android