У меня проблема с перезагрузкой активности с вкладками и фрагментами, когда я меняю ориентацию своего устройства.
Вот ситуация:
У меня есть активность, которая имеет 3 вкладки на панели действий. Каждая вкладка загружает другой фрагмент в FrameLayout
в главном представлении. Все работает нормально, если я не меняю ориентацию устройства. Но когда я это делаю, Android дважды пытается инициализировать текущий выбранный фрагмент, что приводит к следующей ошибке:
E/AndroidRuntime(2022): Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class fragment
Вот последовательность шагов, которые приводят к ошибке:
- Я загружаю активность, выбираю вкладку № 2 и меняю ориентацию устройства.
- Android уничтожает активность и экземпляр фрагмента, загруженного вкладкой № 2 (далее «Фрагмент 2»). Затем он переходит к созданию новых экземпляров действия и фрагмента.
- Внутри
Activity.onCreate()
я добавляю первую вкладку на панель действий. Когда я это делаю, эта вкладка автоматически выбирается. Это может стать проблемой в будущем, но сейчас я не возражаю против этого.onTabSelected
вызывается, и создается и загружается новый экземпляр первого фрагмента (см. код ниже). - Я добавляю все остальные вкладки без запуска каких-либо событий, и это нормально.
- Я звоню
ActionBar.selectTab(myTab)
, чтобы выбрать вкладку № 2. onTabUnselected()
вызывается для первой вкладки, а затемonTabSelected()
для второй вкладки. Эта последовательность заменяет текущий фрагмент экземпляром фрагмента 2 (см. код ниже).- Затем вызывается
Fragment.onCreateView()
для экземпляра Фрагмента 2, и макет фрагмента увеличивается. - Вот в чем проблема. Android вызывает
onCreate()
, а затемonCreateView()
для экземпляра фрагмента ЕЩЕ РАЗ, что создает исключение, когда я пытаюсь раздуть (второй раз) макет.
Очевидно, проблема в том, что Android дважды инициализирует фрагмент, но я не знаю, почему.
Я пробовал НЕ выбирать вторую вкладку при перезагрузке действия, но второй фрагмент все равно инициализируется и не отображается (поскольку я не выбрал его вкладку).
Я нашел этот вопрос: Фрагменты Android воссозданы при изменении ориентации
Пользователь спрашивает в основном то же самое, что и я, но мне не нравится выбранный ответ (это всего лишь обходной путь). Должен быть какой-то способ заставить это работать без трюка android:configChanges
.
В случае, если не ясно, что я хочу знать, как предотвратить воссоздание фрагмента или избежать его двойной инициализации. Было бы неплохо узнать, почему так происходит. :П
Вот соответствующий код:
public class MyActivity extends Activity implements ActionBar.TabListener {
private static final String TAG_FRAGMENT_1 = "frag1";
private static final String TAG_FRAGMENT_2 = "frag2";
private static final String TAG_FRAGMENT_3 = "frag3";
Fragment frag1;
Fragment frag2;
Fragment frag3;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// my_layout contains a FragmentLayout inside
setContentView(R.layout.my_layout);
// Get a reference to the fragments created automatically by Android
// when reloading the activity
FragmentManager fm = getFragmentManager();
this.frag1 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_1);
this.frag2 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_2);
this.frag3 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_3)
ActionBar actionBar = getActionBar();
// snip...
// This triggers onTabSelected for the first tab
actionBar.addTab(actionBar.newTab()
.setText("Tab1").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_1));
actionBar.addTab(actionBar.newTab()
.setText("Tab2").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_2));
actionBar.addTab(actionBar.newTab()
.setText("Tab3").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_3));
Tab t = null;
// here I get a reference to the tab that must be selected
// snip...
// This triggers onTabUnselected/onTabSelected
ab.selectTab(t);
}
@Override
protected void onDestroy() {
// Not sure if this is necessary
this.frag1 = null;
this.frag2 = null;
this.frag3 = null;
super.onDestroy();
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
if (curFrag == null) {
curFrag = createFragmentInstanceForTag(tab.getTag().toString());
if(curFrag == null) {
// snip...
return;
}
}
ft.replace(R.id.fragment_container, curFrag, tab.getTag().toString());
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
if (curFrag == null) {
// snip...
return;
}
ft.remove(curFrag);
}
private Fragment getFragmentInstanceForTag(String tag)
{
// Returns this.frag1, this.frag2 or this.frag3
// depending on which tag was passed as parameter
}
private Fragment createFragmentInstanceForTag(String tag)
{
// Returns a new instance of the fragment requested by tag
// and assigns it to this.frag1, this.frag2 or this.frag3
}
}
Код фрагмента не имеет значения, он просто возвращает завышенное представление при переопределении метода onCreateView()
.
inline code
, жирного шрифта (но не слишком много) и даже html-заголовков (‹h3›‹/h3›) может помочь организовать вашу публикацию... но не злоупотребляйте этим слишком сильно. :П - person Alex Lockwood   schedule 25.06.2012onTabSelected
иonTabUnselected
- person StuStirling   schedule 25.06.2012