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


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.