Як я можу підтримувати стан фрагмента при додаванні до задньої стеки?


160

Я записав фіктивну діяльність, яка перемикається між двома фрагментами. Коли ви переходите від FragmentA до FragmentB, FragmentA додається до заднього стека. Однак, коли я повертаюся до FragmentA (натискаючи назад), створюється абсолютно новий FragmentA, і стан, в якому він був, втрачається. У мене виникає відчуття, що я проживаю те саме, що і це питання, але я включив повний зразок коду, щоб допомогти усунути проблему:

public class FooActivity extends Activity {
  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentA());
    transaction.commit();
  }

  public void nextFragment() {
    final FragmentTransaction transaction = getFragmentManager().beginTransaction();
    transaction.replace(android.R.id.content, new FragmentB());
    transaction.addToBackStack(null);
    transaction.commit();
  }

  public static class FragmentA extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      final View main = inflater.inflate(R.layout.main, container, false);
      main.findViewById(R.id.next_fragment_button).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
          ((FooActivity) getActivity()).nextFragment();
        }
      });
      return main;
    }

    @Override public void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save some state!
    }
  }

  public static class FragmentB extends Fragment {
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.b, container, false);
    }
  }
}

Додано кілька повідомлень журналу:

07-05 14:28:59.722 D/OMG     ( 1260): FooActivity.onCreate
07-05 14:28:59.742 D/OMG     ( 1260): FragmentA.onCreateView
07-05 14:28:59.742 D/OMG     ( 1260): FooActivity.onResume
<Tap Button on FragmentA>
07-05 14:29:12.842 D/OMG     ( 1260): FooActivity.nextFragment
07-05 14:29:12.852 D/OMG     ( 1260): FragmentB.onCreateView
<Tap 'Back'>
07-05 14:29:16.792 D/OMG     ( 1260): FragmentA.onCreateView

Він ніколи не викликає FragmentA.onSaveInstanceState, і він створює новий FragmentA, коли ви повертаєтесь назад. Однак, якщо я перебуваю на FragmentA і блокую екран, FragmentA.onSaveInstanceState дійсно викликається. Так дивно ... чи я помиляюся, очікуючи, що фрагмент, доданий до задньої стопки, не потребує повторного створення? Ось що кажуть документи :

Оскільки, якщо ви видаєте addToBackStack () під час видалення фрагмента, фрагмент зупиняється і буде відновлено, якщо користувач повернеться назад.


3
@ Ян-Хенк Що з речами, які потрібно знайти? Наприклад, місце прокрутки a ListView. Здається, занадто багато стрибків з обручем, щоб приєднати прослуховувач прокрутки та оновити змінну примірника.
Джейк Уортон

2
@JakeWharton Я погоджуюся, що це повинно бути простіше, але, наскільки я знаю, немає способу цього обійтися, тому що onCreateView викликається, коли фрагмент відновлюється з backstack. Але я можу помилитися :)
Jan-Henk

1
onCreate не дзвонить. Отже, мабуть, він повторно використовує той самий примірник, але знову викликає onCreateView? Кульгав. Я думаю, що я можу просто кешувати результат onCreateView і просто повернути існуючий вигляд, якщо onCreateView знову буде викликано.
Ерік

1
Саме те, що я шукав годинами. Чи можете ви опублікувати, як ви цього досягли, використовуючи змінну екземпляра?
Ума

1
Тому я нещодавно розпочав власну реалізацію в github.com/frostymarvelous/Folio і зіткнувся з проблемою. Я в змозі створити близько 5 складних Сторінок / Фрагментів, перш ніж почати отримувати збої OOM. Ось що мене привело сюди. Ховати і показувати просто недостатньо. Перегляди занадто важкі в пам'яті.
frostymarvelous

Відповіді:


120

Якщо ви повернетесь до фрагменту із задньої стеки, він не створить фрагмент, а повторно використовує той самий екземпляр і починається з onCreateView()життєвого циклу фрагмента, див. Життєвий цикл фрагмента .

Отже, якщо ви хочете зберігати стан, ви повинні використовувати змінні екземпляра, а не покладатися на них onSaveInstanceState().


32
Поточна версія документації суперечить цій заяві. Блок-схема говорить про те, що ви заявляєте, але текст у головній частині сторінки каже onCreateView () викликається лише при першому відображенні фрагмента: developer.android.com/guide/components/fragments.html Я борюся з цим проблему зараз, і я не бачу жодних методів, що викликаються при поверненні фрагмента з backstack. (Android 4.2)
Колін М.

10
Намагався записати свою поведінку. OnCreateView () завжди викликається, коли фрагмент відображається.
princepiero

4
@ColinM. Будь-яке рішення проблеми?
хуртовина

9
Це не працює для мене. Мої змінні екземпляра є нульовими при поверненні до фрагмента! Як я можу врятувати державу?
Дон Роммі

5
тому, якщо ми не повинні ретранслювати на екземпляр збереження, як слід зберігати статус фрагмента та дані?
Махді

80

У порівнянні з Apple, UINavigationControllerта UIViewControllerGoogle не дуже добре працює в архітектурі програмного забезпечення Android. І документ про Android Fragmentне дуже допомагає.

Коли ви вводите FragmentB з FragmentA, існуючий екземпляр FragmentA не знищується. Коли ви натискаєте Назад у FragmentB і повертаєтесь до FragmentA, ми не створюємо новий екземпляр FragmentA. onCreateView()Буде викликано існуючий екземпляр FragmentA .

Ключовим є те, що ми не повинні знову завищувати перегляд у FragmentA onCreateView(), тому що ми використовуємо наявний екземпляр FragmentA. Нам потрібно зберегти і повторно використовувати rootView.

Наступний код працює добре. Він не тільки підтримує стан фрагмента, але і зменшує навантаження оперативної пам’яті та процесора (оскільки ми лише надуваємо макет, якщо це необхідно). Я не можу повірити, що зразок коду та документа Google ніколи не згадують його, але завжди надувають макет .

Версія 1 (Не використовуйте версію 1. Використовуйте версію 2)

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // (it will be added back).
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        return _rootView;
    }
}

------ Оновлення 3 травня 2005 року: -------

Як зазначалося в коментарях, іноді _rootView.getParent()це недійсне значення onCreateView, що спричиняє збій. Версія 2 видаляє _rootView в onDestroyView (), як запропонував dell116. Тестовано на Android 4.0.3, 4.4.4, 5.1.0.

Версія 2

public class FragmentA extends Fragment {
    View _rootView;
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (_rootView == null) {
            // Inflate the layout for this fragment
            _rootView = inflater.inflate(R.layout.fragment_a, container, false);
            // Find and setup subviews
            _listView = (ListView)_rootView.findViewById(R.id.listView);
            ...
        } else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove _rootView from the existing parent view group
            // in onDestroyView() (it will be added back).
        }
        return _rootView;
    }

    @Override
    public void onDestroyView() {
        if (_rootView.getParent() != null) {
            ((ViewGroup)_rootView.getParent()).removeView(_rootView);
        }
        super.onDestroyView();
    }
}

УВАГА!!!

Це ХАК! Хоча я використовую його у своєму додатку, вам потрібно тестувати та читати коментарі уважно.


38
Вказівка ​​на кореневий вигляд цілого фрагмента - погана ідея ІМО. Якщо ви постійно додаєте кілька фрагментів до backstack, і всі вони тримають його rootview (який має досить великий слід пам’яті), то існує велика ймовірність, що ви закінчите OutOfMemoryError, оскільки всі фрагменти містять посилання rootview та GC cant збирати його. Я думаю, що кращий підхід полягає в тому, щоб весь час надувати подання (і дозволити системі Android обробляти його створення / знищення), а onActivityCreate / onViewCreate перевірити, чи ваші дані недійсні. Якщо так, то завантажте його, інакше встановіть дані на представлення даних.
traninho

15
Не робіть цього! Коли створена ієрархія перегляду фрагмента, вона містить внутрішню посилання на активність, яка містила фрагмент у той час. Коли відбувається зміна конфігурації, Діяльність часто створюється заново. Повторне використання старого макета зберігає цю активність зомбі навколо пам’яті, а також об’єкти, на які вона також посилається. Витрата пам’яті на зразок цього перешкоджає продуктивності та робить ваш додаток найкращим кандидатом для негайного припинення, коли він не на передньому плані.
Крилез

4
@AllDayAmazing Це хороший момент. Якщо чесно, я зараз дуже розгублений. Хтось може спробувати пояснити, чому не посилатися на посилання на фрагмент rootview фрагмента не нормально, але мати посилання лише на будь-яку дитину rootview (яка так чи інакше має посилання на rootview)?
traninho

2
ЗАСТОСУЙТЕ З Цього, якщо ви не хочете витрачати 5 годин на з'ясування того, що викидає ваш код ..... тоді лише щоб з'ясувати, що це причина. Тепер мені доведеться переробляти купу речей, тому що я використовував цей злом. Набагато краще використовувати fragmentTransaction.add, якщо ви хочете зберегти інтерфейс інтерфейсу фрагмента у такт, коли інший переглядається (навіть зверху). fragmentTransaction.replace () призначений для руйнування поглядів фрагмента ..... не боротися з системою.
dell116

2
@VinceYuan - я перевірив останню бібліотеку v7-appcompat на Android 5.1, і це залишило 6 примірників фрагмента, який слід було видалити в FragmentManager моєї діяльності. Навіть якщо GC буде справлятися з цим правильно (що, я не вірю, це станеться), це спричиняє зайве навантаження на пам’ять як для вашої програми, так і для пристрою взагалі. Просто використання .add () повністю усуває необхідність у всьому цьому хакі-коді. Це повністю суперечить тому, що в першу чергу малося робити використання FragmentTransaction.replace ().
dell116

53

Я думаю, є альтернативний спосіб досягти того, що ви шукаєте. Я не кажу, що це є повним рішенням, але це послужило меті в моєму випадку.

Що я зробив, це замість того, щоб замінити фрагмент, я щойно додав цільовий фрагмент. Тому в основному ви будете використовувати add()метод замість цього replace().

Що ще я зробив. Я приховую свій поточний фрагмент, а також додаю його в backstack.

Отже, він перекриває новий фрагмент над поточним фрагментом, не руйнуючи його погляд. (Переконайтеся, що його onDestroyView()метод не викликається. Плюс додавання його backstateдає мені перевагу відновити фрагмент.

Ось код:

Fragment fragment=new DestinationFragment();
FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction ft=fragmentManager.beginTransaction();
ft.add(R.id.content_frame, fragment);
ft.hide(SourceFragment.this);
ft.addToBackStack(SourceFragment.class.getName());
ft.commit();

Система AFAIK дзвонить лише в тому onCreateView()випадку, якщо представлення знищено або не створено. Але тут ми зберегли подання, не видаляючи його з пам'яті. Таким чином, це не створить новий погляд.

І коли ви повернетеся з фрагмента призначення, він з’явиться останній FragmentTransactionвилучений верхній фрагмент, який зробить верхній (SourceFragment) вид над екраном.

КОМЕНТАР: Як я вже сказав, це не повне рішення, оскільки воно не видаляє вигляд фрагмента Source і, отже, займає більше пам'яті, ніж зазвичай. Але все-таки слугуйте меті. Крім того, ми використовуємо абсолютно інший механізм приховування подання замість того, щоб замінити його, який є нетрадиційним.

Тож справа не в тому, як ви підтримуєте стан, а в тому, як ви підтримуєте погляд.


У моєму випадку додавання фрагмента замість заміни викликає проблему при використанні опитування або будь-якого іншого веб-запиту. Я хочу призупинити це опитування у фрагменті A, коли додано фрагмент B. Будь-яка ідея з цього приводу?
Ума

Як ви використовуєте опитування в FirstFragment? Це потрібно зробити вручну, оскільки обидва фрагменти залишаються в пам'яті. Отже, ви можете використовувати їх екземпляри для виконання необхідних дій. Це підказка, генеруючи подію в основній діяльності, яка робить щось, коли ви додаєте другий фрагмент. Сподіваюсь, це допоможе.
kaushal trivedi

1
Дякую за підказку =). Я це зробив. Але це єдиний спосіб зробити це? І відповідний спосіб? Також коли я натискаю кнопку додому та знову запускаю програму, всі фрагменти знову активуються. Припустимо, я тут, у фрагменті В, таким чином. Activity A{Fragment A --> Fragment B}коли я знову запускаю програму після натискання кнопки додому, onResume()викликається обидва фрагменти, і тому вони починають опитування. Як я можу це контролювати?
Ума

1
На жаль, ви не можете, Система не працює в нормальній поведінці таким чином, вона буде вважати обидва фрагменти прямими дітьми діяльності. Хоча це і слугувало метою підтримання стану фрагмента, іншими нормальними речами стає дуже важко керувати. Останнім часом я Виявив усі ці проблеми, тепер моя пропозиція не йти цим шляхом.
kaushal trivedi

1
Звичайно, нарешті я скажу не піти з таким підходом, поки не знайдете будь-яке інше рішення. Тому що це важко керувати.
kaushal trivedi

7

Я б запропонував дуже просте рішення.

Візьміть довідкову змінну View і встановіть подання в OnCreateView. Перевірте, чи існує перегляд у цій змінній, а потім поверніть той самий вигляд.

   private View fragmentView;

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);

        if (fragmentView != null) {
            return fragmentView;
        }
        View view = inflater.inflate(R.layout.yourfragment, container, false);
        fragmentView = view;
        return view;
    }

1
Є ймовірність витоку пам'яті, якщо ми не видалимо змінну 'fragmentView' в onDestroy ()
Arun PM

@ArunPM так як видалити fragmentView в onDestroy ()? if (_rootView.getParent() != null) { ((ViewGroup)_rootView.getParent()).removeView(_rootView); }підходить для очищення пам'яті?
Мехмет Гюр

1
@ MehmetGür Я багато разів використовую це рішення. До цього часу я не отримав жодної помилки у витоку пам'яті. Але ви можете використовувати рішення ArunPM з цим рішенням, якщо хочете. Я думаю, що він пропонує встановити fragmentView на нуль методом OnDestroy ().
Mandeep Singh

1
Я використовую LeakCanary для виявлення витоків пам’яті та її викидів, коли я дотримувався цього методу. Але , як @Mandeep зітхати згадується в коментарях , ми можемо подолати цю проблему шляхом присвоєння nullдо fragmentView змінному в onDestroy()методі.
Arun PM

1
Згідно з моїми знаннями, коли фрагмент, який отримує, руйнується, вигляд, пов'язаний з фрагментом, очищається onDestroyView(). Це очищення не відбувається для нашої змінної перегляду резервного копіювання (тут fragmentView ), і це призведе до витоку пам’яті, коли фрагмент назад складений / знищений. Це ж посилання ви можете знайти у [Загальні причини витоків пам’яті] ( square.github.io/leakcanary/fundamentals/… ) у вступі LeakCanery.
Arun PM

6

Я зіткнувся з цією проблемою у Фрагменті, що містить карту, яка містить занадто багато деталей налаштування, щоб зберегти / перезавантажити. Моє рішення полягало в тому, щоб цей Фрагмент весь час був активним (подібно до того, що згадував @kaushal).

Скажімо, у вас є поточний фрагмент A і він хоче відобразити фрагмент B. Підсумовуючи наслідки:

  • Замінити () - видаліть Фрагмент А та замініть його Фрагмент В. Фрагмент А буде відтворений, коли його знову винесуть на передову
  • add () - (створити та) додати фрагмент B і перекрити фрагмент A, який все ще активний у фоновому режимі
  • delete () - може бути використаний для видалення фрагмента B і повернення до A. Фрагмент B буде відтворений, коли буде викликаний пізніше

Отже, якщо ви хочете зберегти обидва фрагмента "збереженими", просто перемкніть їх, використовуючи функцію hid () / show ().

Плюси : простий і простий метод збереження декількох фрагментів, які працюють :
Мінуси : ви використовуєте набагато більше пам’яті, щоб тримати їх усі. Може виникнути проблеми, наприклад, показувати багато великих растрових зображень


Ви можете, будь ласка, сказати мені, коли ми видаляємо фрагмент b і повертаємось до A, який метод викликається у фрагменті A? я хочу вжити певних дій, коли ми видалимо фрагмент B.
Google

5

onSaveInstanceState() викликається лише у випадку зміни конфігурації.

Оскільки змінюється один фрагмент на інший, зміна конфігурації не відбувається, тому виклику onSaveInstanceState()немає. Який стан не економлять? Ви можете вказати?

Якщо ви введете якийсь текст у EditText, він буде збережений автоматично. Будь-який елемент інтерфейсу без жодного ідентифікатора - це той елемент, стан перегляду якого не зберігається.


onSaveInstanceState()називається також тоді, коли система знищує діяльність, оскільки їй бракує ресурсів.
Marcel Bro

0

Тут, оскільки onSaveInstanceStateфрагмент не дзвонить, коли ви додаєте фрагмент у backstack. Життєвий цикл фрагмента в backstack, коли відновлено початок onCreateViewі кінець, onDestroyViewпоки onSaveInstanceStateвикликається між onDestroyViewі onDestroy. Моє рішення - створити змінну екземпляра та init in onCreate. Приклад коду:

private boolean isDataLoading = true;
private ArrayList<String> listData;
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     isDataLoading = false;
     // init list at once when create fragment
     listData = new ArrayList();
}

І перевірте це onActivityCreated:

public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(isDataLoading){
         fetchData();
    }else{
         //get saved instance variable listData()
    }
}

private void fetchData(){
     // do fetch data into listData
}

0
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener()
    {
        @Override
        public void onBackStackChanged()
        {
            if (getSupportFragmentManager().getBackStackEntryCount() == 0)
            {
                //setToolbarTitle("Main Activity");
            }
            else
            {
                Log.e("fragment_replace11111", "replace");
            }
        }
    });


YourActivity.java
@Override
public void onBackPressed()
{
 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.Fragment_content);
  if (fragment instanceof YourFragmentName)
    {
        fragmentReplace(new HomeFragment(),"Home Fragment");
        txt_toolbar_title.setText("Your Fragment");
    }
  else{
     super.onBackPressed();
   }
 }


public void fragmentReplace(Fragment fragment, String fragment_name)
{
    try
    {
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.Fragment_content, fragment, fragment_name);
        fragmentTransaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
        fragmentTransaction.addToBackStack(fragment_name);
        fragmentTransaction.commitAllowingStateLoss();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

0

Моя проблема була схожа, але мене подолали, не зберігаючи фрагмент живим. Припустимо, у вас є діяльність, яка має 2 фрагменти - F1 і F2. F1 запускається спочатку і дозволяє сказати, що містить деяку інформацію про користувача, а потім за певної умови F2 з'являється на запиті користувача заповнити додатковий атрибут - їх номер телефону. Далі ви хочете, щоб цей номер телефону повернувся до F1 та завершився реєстрацією, але ви усвідомлюєте, що вся попередня інформація про користувача втрачена і у вас немає їх попередніх даних. Фрагмент відтворено з нуля, і навіть якщо ви зберегли цю інформацію в onSaveInstanceStateпакеті, він повернеться нульовим onActivityCreated.

Рішення: Збережіть необхідну інформацію як змінну екземпляра в діяльності виклику. Потім передайте цю змінну екземпляра у свій фрагмент.

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Bundle args = getArguments();

    // this will be null the first time F1 is created. 
    // it will be populated once you replace fragment and provide bundle data
    if (args != null) {
        if (args.get("your_info") != null) {
            // do what you want with restored information
        }
    }
}

Отже, далі з мого прикладу: перед тим, як відобразити F2, я зберігаю дані користувача у змінній екземпляра за допомогою зворотного дзвінка. Потім я запускаю F2, користувач заповнює номер телефону і натискає збереження. Я використовую інший зворотний виклик у діяльності, збираю цю інформацію та замінюю мій фрагмент F1, на цей раз він містить дані про пакет, які я можу використовувати.

@Override
public void onPhoneAdded(String phone) {
        //replace fragment
        F1 f1 = new F1 ();
        Bundle args = new Bundle();
        yourInfo.setPhone(phone);
        args.putSerializable("you_info", yourInfo);
        f1.setArguments(args);

        getFragmentManager().beginTransaction()
                .replace(R.id.fragmentContainer, f1).addToBackStack(null).commit();

    }
}

Більше інформації про зворотні дзвінки можна знайти тут: https://developer.android.com/training/basics/fragments/communicating.html


0

по-перше : просто використовуйте метод add замість методу заміни класу FragmentTransaction, тоді вам потрібно додати secondFragment до стека методом addToBackStack

друге : при зворотному натисканні потрібно викликати popBackStackImmediate ()

Fragment sourceFragment = new SourceFragment ();
final Fragment secondFragment = new SecondFragment();
final FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ft.add(R.id.child_fragment_container, secondFragment );
ft.hide(sourceFragment );
ft.addToBackStack(NewsShow.class.getName());
ft.commit();
                                
((SecondFragment)secondFragment).backFragmentInstanceClick = new SecondFragment.backFragmentNewsResult()
{
        @Override
        public void backFragmentNewsResult()
        {                                    
            getChildFragmentManager().popBackStackImmediate();                                
        }
};

0

Замініть фрагмент, використовуючи наступний код:

Fragment fragment = new AddPaymentFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.frame, fragment, "Tag_AddPayment")
                .addToBackStack("Tag_AddPayment")
                .commit();

Діяльність onBackPress ():

  @Override
public void onBackPressed() {
    android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
    if (fm.getBackStackEntryCount() > 1) {

        fm.popBackStack();
    } else {


        finish();

    }
    Log.e("popping BACKSTRACK===> ",""+fm.getBackStackEntryCount());

}

0
Public void replaceFragment(Fragment mFragment, int id, String tag, boolean addToStack) {
        FragmentTransaction mTransaction = getSupportFragmentManager().beginTransaction();
        mTransaction.replace(id, mFragment);
        hideKeyboard();
        if (addToStack) {
            mTransaction.addToBackStack(tag);
        }
        mTransaction.commitAllowingStateLoss();
    }
replaceFragment(new Splash_Fragment(), R.id.container, null, false);

1
Дякуємо за цей фрагмент коду, який може надати деяку обмежену та негайну допомогу. Належне пояснення було б значно поліпшити свою довгострокову цінність , показуючи , чому це хороше рішення проблеми, і зробить його більш корисним для читачів майбутніх з іншими подібними питаннями. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, включаючи зроблені вами припущення.
Machavity

0

Ідеальне рішення, яке знаходить старий фрагмент у стеці та завантажує його, якщо він існує у стеці.

/**
     * replace or add fragment to the container
     *
     * @param fragment pass android.support.v4.app.Fragment
     * @param bundle pass your extra bundle if any
     * @param popBackStack if true it will clear back stack
     * @param findInStack if true it will load old fragment if found
     */
    public void replaceFragment(Fragment fragment, @Nullable Bundle bundle, boolean popBackStack, boolean findInStack) {
        FragmentManager fm = getSupportFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        String tag = fragment.getClass().getName();
        Fragment parentFragment;
        if (findInStack && fm.findFragmentByTag(tag) != null) {
            parentFragment = fm.findFragmentByTag(tag);
        } else {
            parentFragment = fragment;
        }
        // if user passes the @bundle in not null, then can be added to the fragment
        if (bundle != null)
            parentFragment.setArguments(bundle);
        else parentFragment.setArguments(null);
        // this is for the very first fragment not to be added into the back stack.
        if (popBackStack) {
            fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        } else {
            ft.addToBackStack(parentFragment.getClass().getName() + "");
        }
        ft.replace(R.id.contenedor_principal, parentFragment, tag);
        ft.commit();
        fm.executePendingTransactions();
    }

використовувати його як

Fragment f = new YourFragment();
replaceFragment(f, null, boolean true, true); 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.