Портретное расположение не работает, когда действие начинается в пейзаже

Это уменьшенная версия актуальной проблемы. Чтобы воссоздать трудности, с которыми я сталкиваюсь.

Я взял пример с официального сайта developer.android.com процитировать мою проблему. Создание гибкого интерфейса

MainActivity имеет 2 макета. Один для макета по умолчанию (портрет на устройствах с маленьким экраном) в layout папка. Другой макет для большого экрана и альбомной ориентации, сохраненный в layout-large а также layout-land папка.

Макет по умолчанию для activity_main.xml содержит только один FrameLayout (R.id.fragment_container), в котором я добавляю и заменяю 2 фрагмента, которые я создаю, динамически.

Другое расположение одинаково для обоих layout-land а также layout-large папки. Имеет 2 статических фрагмента [R.id.headlines_fragment - отобразить список заголовков] и [R.id.article_fragment - чтобы отобразить детали, когда заголовки выбраны]. Горизонтально расположен. Один слева, чтобы показать списки и один справа, чтобы показать детали.

Это код для MainActivity.java который контролирует все фрагменты:

public class MainActivity extends Activity implements OnHeadLineSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_article);          

        if(findViewById(R.id.fragment_container) != null) {
            if(savedInstanceState != null) {
                return;
            }

            HeadlinesFragment firstFragment = new HeadlinesFragment();
            firstFragment.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();
        }
    }

    @Override
    public void onArticleSelected(int position) {
        ArticleFragment articleFrag = (ArticleFragment) getFragmentManager().findFragmentById(R.id.article_fragment);
        if(articleFrag != null && articleFrag.isVisible()) {
            articleFrag.updateArticleView(position);
        } else {

            articleFrag = new ArticleFragment();
            FragmentTransaction transaction = getFragmentManager().beginTransaction().replace(R.id.fragment_container, articleFrag);
            transaction.addToBackStack(null);
            transaction.commit();

        }
    }       
}

Как только начинается действие, я проверяю, fragment_container это FrameLayout присутствует или нет. Если его нет, то загружен макет с двумя фрагментами. Следовательно, нет необходимости добавлять фрагменты динамически, поскольку они уже присутствуют.

В противном случае я проверяю, savedInstanceState является нулевым или нет. Если ноль, то я создаю новый HeadlinesFragment и добавьте его в кадр. Если оно не равно нулю, то это означает, что действие уже было создано ранее, и, следовательно, HeadlinesFragment должно быть уже добавлено. Нет необходимости добавлять его снова. Так, return,

А также onArticleSelected() метод заменяет существующий фрагмент в кадре ArticleFragmentили, если в другом макете, он просто обновляет фрагмент, поскольку он уже присутствует. Это называется из HeadlinesFragment когда элемент выбран.

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

Но если мы введем деятельность в landscape режиме, как только я меняю ориентацию на портретный режим, отображается пустой экран.

Причина в том, что onCreate() называется, а savedInstanceState возвращается как нет null, Следовательно HeadlinesFragment не создается и не добавляется в кадр.

И да, если я уберу эту проверку, то приложение будет работать нормально, но это будет означать, что новый HeadlinesFragment создается и добавляется в кадр каждый раз и накладывается друг на друга. Что совсем не желательно.

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

Я перепробовал много запутанной логики. Но ничего не работает. Любая помощь приветствуется.

Ввод активности в портретном режиме

1> Элементы списка показаны.

2> Нажатие элементов заменяет фрагмент на ArticleFragment (подробности).

3> Изменение ориентации, показывает обе стороны рядом. Все работает.

ЗаголовкиФрагмент показан ---> ArticleFragment отображается при нажатии на элемент списка

Оба показаны в ландшафтном режиме

Ввод активности в ландшафтном режиме

1> И список и детали показаны. Все работает.

2> Но как только ориентация изменится, вы получите пустой экран. Фрагмент заголовков не создается и не добавляется.

Ввод активности в ландшафтном режиме -> ЗаголовкиФрагмент не показан. Ничего не загружает. Пустой экран

Было бы очень полезно, если бы кто-то мог направить меня в том, как я могу решить эту проблему. А так как реальный проект огромен, и эта логика уже реализована, радикальное изменение в логике больше не вариант, поскольку это будет означать перезапись тысяч строк кода. Спасибо.:)

3 ответа

Хорошо. У меня проблема. Проблема в том, что мы используем Fragments в макетах XML в больших устройствах.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

Так что Android пытается поймать фрагменты в savedInstanceState в основной деятельности. Когда экран поворачивается, система пытается восстановить вышеуказанные фрагменты, даже если в режиме миниатюр загружается другой макет. И поэтому система считает, что article_fragment также доступно на правой стороне, и он пытается обновить его по клику на заголовок.

Итак, каково решение?

Я немного изменил код в MainActivity и больше ничего:-)

/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(null);
        setContentView(R.layout.news_articles);

        // Check whether the activity is using the layout version with
        // the fragment_container FrameLayout. If so, we must add the first fragment
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            /*if (savedInstanceState != null) {
                return;
            }*/

            // Create an instance of ExampleFragment
            HeadlinesFragment firstFragment = new HeadlinesFragment();

            // In case this activity was started with special instructions from an Intent,
            // pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());

            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }

Так что я только что сказал системе, что не хочу ничего восстанавливать с помощью super.onCreate(null); так что сейчас ничего не восстанавливается.

О пустом экране вы получаете

Всякий раз, когда вы начинаете деятельность в ландшафтном режиме. Он загружает большой дисплей по умолчанию. Не входя в если satement. Потому что это не может получить fragment_container в ландшафтном режиме. А затем вы поворачиваете экран, чтобы загрузить макет портрета, и система получает savedInstanceState != null и возвращается без загрузки HeadlinesFragment, Таким образом, вы получаете экран банка.

Поэтому я прокомментировал заявление If, как вы можете заметить.

/*if (savedInstanceState != null) {
                    return;
  }*/

Так что теперь он загружает все правильно. Нет проблем

Загрузите образец кода с этого сайта разработчика и измените его в соответствии с вашими потребностями.

Построить динамический интерфейс с фрагментами

Это супер и надежный код.

Я уверен, что есть причины, по которым Fragment было правильно отображено в Portrait -> Landscape и не наоборот, но одна вещь очень ясно о Activity жизненный цикл

Вызывается, когда начинается действие. Это где большинство инициализации должно идти: вызов setContentView(int) раздувать пользовательский интерфейс, используя findViewById(int) программно взаимодействовать с виджетами в пользовательском интерфейсе, вызывая managedQuery(android.net.Uri, String[], String, String[], String) извлекать курсоры для отображаемых данных и т. д.

Обратите внимание на часть, которая говорит, что это то место, где должна идти основная инициализация. Выходя после проверки этого savedInstanceState ноль, вы оставляете это до super класс, чтобы восстановить свой Fragment что не очень хорошая идея, учитывая, что предыдущая точка зрения была разрушена.

Мой совет, проверить содержание savedInstanceState вместо того, чтобы просто проверить, является ли оно нулевым. Убедитесь, что он содержит достаточно данных, чтобы восстановить ваше предыдущее состояние, если не инициализировать ваш фрагмент.

Лучшие практики с фрагментами включают в себя реализацию всех необходимых методов для мониторинга состояния.

Другие вопросы по тегам