Портретное расположение не работает, когда действие начинается в пейзаже
Это уменьшенная версия актуальной проблемы. Чтобы воссоздать трудности, с которыми я сталкиваюсь.
Я взял пример с официального сайта 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> Изменение ориентации, показывает обе стороны рядом. Все работает.
Ввод активности в ландшафтном режиме
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
вместо того, чтобы просто проверить, является ли оно нулевым. Убедитесь, что он содержит достаточно данных, чтобы восстановить ваше предыдущее состояние, если не инициализировать ваш фрагмент.
Лучшие практики с фрагментами включают в себя реализацию всех необходимых методов для мониторинга состояния.