Как я могу переключаться между двумя фрагментами, не каждый раз воссоздавая фрагменты?
Я работаю над андроидным приложением, которое использует навигационный ящик для переключения между двумя фрагментами. Однако каждый раз, когда я переключаюсь, фрагмент полностью воссоздается.
Вот код из моей основной деятельности.
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
android.support.v4.app.Fragment fragment;
String tag;
android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();
switch(position) {
case 0:
if(fragmentManager.findFragmentByTag("one") != null) {
fragment = fragmentManager.findFragmentByTag("one");
} else {
fragment = new OneFragment();
}
tag = "one";
break;
case 1:
if(fragmentManager.findFragmentByTag("two") != null) {
fragment = fragmentManager.findFragmentByTag("two");
} else {
fragment = new TwoFragment();
}
tag = "two";
break;
}
fragment.setRetainInstance(true);
fragmentManager.beginTransaction().replace(R.id.container, fragment, tag).commit();
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
Я установил несколько протоколов отладки, и каждый раз, когда вызывается selectItem, один фрагмент уничтожается, а другой создается.
Есть ли способ предотвратить воссоздание фрагментов и просто повторно использовать их?
Ответы
Ответ 1
После того, как @meredrica указала, что replace() уничтожает фрагменты, я вернулся через документацию FragmentManager. Это решение, с которым я столкнулся, кажется, работает.
/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();
switch(position) {
case 0:
if(fragmentManager.findFragmentByTag("one") != null) {
//if the fragment exists, show it.
fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("one")).commit();
} else {
//if the fragment does not exist, add it to fragment manager.
fragmentManager.beginTransaction().add(R.id.container, new OneFragment(), "one").commit();
}
if(fragmentManager.findFragmentByTag("two") != null){
//if the other fragment is visible, hide it.
fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("two")).commit();
}
break;
case 1:
if(fragmentManager.findFragmentByTag("two") != null) {
//if the fragment exists, show it.
fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("two")).commit();
} else {
//if the fragment does not exist, add it to fragment manager.
fragmentManager.beginTransaction().add(R.id.container, new TwoFragment(), "two").commit();
}
if(fragmentManager.findFragmentByTag("one") != null){
//if the other fragment is visible, hide it.
fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("one")).commit();
}
break;
}
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mNavTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
Я также добавил этот бит, но я не уверен, что это необходимо или нет.
@Override
public void onDestroy() {
super.onDestroy();
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragmentManager.findFragmentByTag("one") != null){
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("one")).commit();
}
if(fragmentManager.findFragmentByTag("two") != null){
fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("two")).commit();
}
}
Ответ 2
Используйте attach/detach метод с тегами:
Отсоединение уничтожит представление hirachy, но сохраняет состояние, как если бы оно было на задней стороне; это позволит "незавидному" фрагменту иметь меньший объем памяти. Но помните, что вам нужно правильно реализовать жизненный цикл фрагмента (который вы должны сделать в первую очередь)
Отсоедините данный фрагмент от пользовательского интерфейса. Это то же самое состояние, что и при наложении на задний стек: фрагмент удаляется из пользовательского интерфейса, однако его состояние по-прежнему активно управляется менеджером фрагментов. При входе в это состояние его иерархия представлений уничтожается.
При первом добавлении фрагмента
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(),MyFragment.class.getSimpleName());
t.commit();
тогда вы отсоедините его
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.detach(MyFragment.class.getSimpleName());
t.commit();
и прикрепите его снова, если он будет включен, состояние будет сохранено
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();
Но вам всегда нужно проверить, добавлен ли фрагмент, если нет, добавьте его, иначе просто присоедините его:
if (getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()) == null) {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(), MyFragment.class.getSimpleName());
t.commit();
} else {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();
}
Ответ 3
Метод замещения уничтожает ваши фрагменты. Один из способов - установить их на Visibility.GONE
, другой (менее простой) метод - удерживать их в переменной. Если вы это сделаете, убедитесь, что вы не просачиваете память влево и вправо.
Ответ 4
Я сделал это раньше:
if (mPrevFrag != fragment) {
// Change
FragmentTransaction ft = fragmentManager.beginTransaction();
if (mPrevFrag != null){
ft.hide(mPrevFrag);
}
ft.show(fragment);
ft.commit();
mPrevFrag = fragment;
}
(вам нужно будет отследить ваш искомый фрагмент в этом решении)
Ответ 5
это немного поздний ответ.
если вы используете просмотр пейджера для фрагментов, установите для параметра безэкранного фрагмента фрагмент количество созданных фрагментов.
mViewPager.setOffscreenPageLimit(3); // number of fragments here is 3
Ответ 6
Я думаю, вы не можете напрямую манипулировать механизмами жизненного цикла вашего Fragments
. Сам факт, что вы можете findFragmentByTag
, не очень плох. Это означает, что объект Фрагмент не воссоздается полностью, если он уже совершен. Существующий Fragment
просто передает все этапы жизненного цикла, каждый из которых имеет Fragment
- это означает, что только пользовательский интерфейс "воссоздан".
Это очень удобная и полезная стратегия управления памятью - и в большинстве случаев она подходит. Fragment
, который ушел, имеет ресурсы, которые необходимо использовать для де-распределения памяти.
Если вы просто прекратите использовать эту стратегию, использование памяти вашего приложения может сильно ухудшиться.
Тем не менее, существуют сохраненные фрагменты, причем этот жизненный цикл немного отличается и не соответствует активности, к которой они привязаны. Как правило, они используются для сохранения некоторых вещей, которые вы хотите сохранить, например, управлять изменениями конфигурации
Однако стратегия создания фрагмента [re] зависит от контекста - то есть того, что вы хотели бы решить, и каковы компромиссы, которые вы готовы принять.
Ответ 7
Та же идея, что и Tester101, но это то, что я использовал.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment oldFragment = fragmentManager.findFragmentByTag( "" + m_lastDrawerSelectPosition );
if ( oldFragment != null )
fragmentTransaction.hide( oldFragment );
Fragment newFragment = fragmentManager.findFragmentByTag( "" + position );
if ( newFragment == null )
{
newFragment = getFragment( position );
fragmentTransaction.add( R.id.home_content_frame, newFragment, "" + position );
}
fragmentTransaction.show( newFragment );
fragmentTransaction.commit();
Ответ 8
Как играть с атрибутом Visible?