Фрагмент onResume () та onPause () не закликається в backstack


196

У мене є кілька фрагментів всередині активності. Після натискання кнопки я запускаю новий фрагмент, додаю його в backstack. Я, звичайно, очікував, що onPause()метод сучасного Фрагменту та onResume()нового Фрагменту буде названий. Ну це не відбувається.

LoginFragment.java

public class LoginFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
      final FragmentManager mFragmentmanager =  getFragmentManager();

      Button btnHome  = (Button)view.findViewById(R.id.home_btn);
      btnHome.setOnClickListener(new View.OnClickListener() {
        public void onClick(View view){
           HomeFragment fragment    = new HomeFragment();
           FragmentTransaction ft2   =  mFragmentmanager.beginTransaction();
           ft2.setCustomAnimations(R.anim.slide_right, R.anim.slide_out_left
                    , R.anim.slide_left, R.anim.slide_out_right);
           ft2.replace(R.id.middle_fragment, fragment);
           ft2.addToBackStack(""); 
           ft2.commit();    
         }
      });
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of LoginFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
    Log.e("DEBUG", "OnPause of loginFragment");
    super.onPause();
  }
}

HomeFragment.java

public class HomeFragment extends Fragment{
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     final View view  =   inflater.inflate(R.layout.login_fragment, container, false);
  }

  @Override
  public void onResume() {
     Log.e("DEBUG", "onResume of HomeFragment");
     super.onResume();
  }

  @Override
  public void onPause() {
     Log.e("DEBUG", "OnPause of HomeFragment");
     super.onPause();
  }
}

Що я очікував:

  1. Коли кнопка натиснута, LoginFragment замінюється HomeFragment , onPause()з LoginFragment , і onResume()з HomeFragment викликається
  2. Коли знову натиснута, HomeFragment буде poped, і LoginFragment видно, і onPause()з HomeFragment і onResume()з LoginFragment викликається.

Що я отримую, це

  1. Після натискання кнопки HomeFragment правильно замінює LoginFragment , викликається onResume () HomeFragment , але onPause () з LoginFragment ніколи не викликається.
  2. Якщо натиснути назад, HomeFragment правильно вискакує, щоб виявити LoginFragment , викликується onPause () HomeFragment , але onResume () LoginFragment ніколи не викликається.

Це нормальна поведінка? Чому onResume()з LoginFragment не викликався , коли я натискаю кнопку назад.


Додайте код активності, який обробляє фрагменти.
— blessenm

У мене є проблема із зразком, після паузи не дзвони, як ти це вирішив,
— Сем

У мене була така ж проблема, але я зрозумів, що використовую ft2.add (); замість ft2.replace (). Єдиною іншою причиною буде, якщо ваша діяльність зберігає посилання на фрагмент (додавши його до колекції або
— віднести

3
У мене така ж проблема. Я помітив, що .replace () викличе необхідні методи життєвого циклу, але це по суті руйнує фрагмент. Крім того, onSaveInstanceState не викликається. Як такий, я не можу утримувати його стан. Отже, мені потрібно використовувати додавання, але onResume / Pause не називається :(
— арії

FWIW, мій досвід полягає в тому, що фрагменти бібліотеки підтримки викликають OnPause та onResume при натисканні / вискакуванні задніх верстатів, але вбудовані фрагменти Android ні. Ще не знайшли належного рішення для цього.
— benkc

Відповіді:


187

Фрагменти onResume()або onPause()будуть називатися лише тоді, коли Заходи onResume()або onPause()називаються. Вони щільно з'єднані зActivity .

Прочитайте розділ Поводження з життєвим циклом фрагмента цієї статті .


1
У статті йдеться про те, що "після того, як активність досягне відновленого стану, ви можете вільно додавати та видаляти фрагменти до діяльності. Таким чином, лише тоді, коли активність перебуває у відновленому стані, життєвий цикл фрагмента може змінюватися незалежно". Чи означає це, що фрагмент onResume можна викликати, навіть якщо активність onResume не викликається?
— v4r

3
що стосується натільних (не підтримуючих) фрагментів у 4.4 (не впевнений, що це правда для старих версій), onPause () та onResume () називаються не лише тоді, коли ці події відбуваються в діяльності, але, наприклад, коли ви викликаєте заміну () або додавання () / delete () під час здійснення транзакцій, тому ця відповідь вводить в оману принаймні для останніх версій Android.
— Dmide

19
Згідно з цим документом, фрагмент повинен бути фактично переміщений у зупинений стан, коли його поміняють у backstack. Але не тільки onPause та onResume не дзвонять, ані onStop та onStart - або, з цього приводу, будь-які інші методи життєвого циклу. Тож посібник, безумовно, вводить в оману.
— benkc

1
Хоча це не пов'язано, але я знайшов це питання під час пошуку своєї проблеми onPause()отримання дзвінка onSaveInstanceState()замість цього. Це можна відтворити, якщо ви до іншої дитини в моєму FragmentStatePagerAdapter(скажімо, ви переходите від дитини 0 до дитини 2, зверніть увагу на себе, це відбувається тому, що дитина 0 знищується, коли дитина 2 відкривається )
— Суфіан,

Не впевнений, що ти маєш на увазі. Виклик ft.replace повинен викликати OnPause (заміненого фрагмента) та onResume (замінюючого фрагмента). Це робиться незалежно від будь-якої діяльності ...
— Девід Рефаелі

20
  • Оскільки ви використовували ft2.replace(), FragmentTransaction.remove() метод викликається і Loginfragmentзаповіт буде видалено. Зверніться до цього . Так onStop()з LoginFragmentназиватиметься замість onPause(). (Оскільки новий фрагмент повністю замінює старий).
  • Але так як ви використовували також ft2.addtobackstack(), стан з Loginfragmentбуде збережено у вигляді пучка і при натисканні кнопки зі спиною HomeFragment, onViewStateRestored()буде називатися потім onStart()з LoginFragment. Тому в підсумку onResume()не зателефонують.

1
onViewStateRestoredназивається, якщо у вас єsetRetainInstance(true)
— Фарид

14

Ось моя більш міцна версія відповіді Гор (використання fragments.size () є ненадійною через те, що розмір не зменшується після появи фрагмента)

getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            if (getFragmentManager() != null) {

                Fragment topFrag = NavigationHelper.getCurrentTopFragment(getFragmentManager());

                if (topFrag != null) {
                    if (topFrag instanceof YourFragment) {
                        //This fragment is being shown. 
                    } else {
                        //Navigating away from this fragment. 
                    }
                }
            }
        }
    });

І метод 'getCurrentTopFragment':

public static Fragment getCurrentTopFragment(FragmentManager fm) {
    int stackCount = fm.getBackStackEntryCount();

    if (stackCount > 0) {
        FragmentManager.BackStackEntry backEntry = fm.getBackStackEntryAt(stackCount-1);
        return  fm.findFragmentByTag(backEntry.getName());
    } else {
        List<Fragment> fragments = fm.getFragments();
        if (fragments != null && fragments.size()>0) {
            for (Fragment f: fragments) {
                if (f != null && !f.isHidden()) {
                    return f;
                }
            }
        }
    }
    return null;
}

9

Якщо ви дійсно хочете замінити фрагмент всередині іншого фрагмента, вам слід скористатися вкладеними фрагментами .

У своєму коді слід замінити

final FragmentManager mFragmentmanager =  getFragmentManager();

з

final FragmentManager mFragmentmanager =  getChildFragmentManager();

5
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            List<Fragment> fragments = getFragmentManager().getFragments();
            if (fragments.size() > 0 && fragments.get(fragments.size() - 1) instanceof YoureFragment){
                //todo if fragment visible
            } else {
                //todo if fragment invisible
            }

        }
    });

але будьте обережні, якщо видно більше одного фрагмента


Дякую, він працює, але якщо видно лише один фрагмент (так, ViewPagerз фрагментами буде посилання на його фрагменти).
— CoolMind

3

У мене код дуже схожий на ваш, і якщо він працює наPause () та onResume (). При зміні фрагмента ці функції активуються відповідно.

Код у фрагменті:

 @Override
public void onResume() {
    super.onResume();
    sensorManager.registerListener(this, proximidad, SensorManager.SENSOR_DELAY_NORMAL);
    sensorManager.registerListener(this, brillo, SensorManager.SENSOR_DELAY_NORMAL);
    Log.e("Frontales","resume");
}

@Override
public void onPause() {
    super.onPause();
    sensorManager.unregisterListener(this);
    Log.e("Frontales","Pause");

}

Журнал при зміні фрагмента:

05-19 22:28:54.284 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:28:57.002 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:28:58.697 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:00.840 2371-2371/madi.cajaherramientas E/Frontales: Pause
05-19 22:29:02.248 2371-2371/madi.cajaherramientas E/Frontales: resume
05-19 22:29:03.718 2371-2371/madi.cajaherramientas E/Frontales: Pause

Фрагмент onCreateView:

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

    rootView = inflater.inflate(R.layout.activity_proximidad, container, false);
    ButterKnife.bind(this,rootView);
    inflar();
    setTextos();
    return rootView;
}

Дія, коли я пульсую назад (у тій діяльності, де я завантажую фрагмент):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();

    } else {
        getFragmentManager().popBackStack();
    }

 }

2

Що я роблю з дочірнього фрагмента:

@Override
public void onDetach() {
   super.onDetach();
   ParentFragment pf = (ParentFragment) this.getParentFragment();
   pf.onResume();
}

А потім перезапишіть onResume на ParentFragment


1
Ніколи не слід називати способи життєвого циклу вручну, особливо всередині один одного
— перерва

@breakline ця методика працює. Чи є у вас інший спосіб?
— Вікаш Параджулі

Так, вам слід додати власну реалізацію для виклику, оскільки методи життєвого циклу також викликаються системою, і якщо ви називаєте способи життєвого циклу всередині один одного, як це, ви можете (і, швидше за все,) спричинити пізні проблеми.
— перерва

1

Ви просто не можете додати фрагмент до фрагмента. Це має відбутися в FragmentActivity. Я припускаю, що ви створюєте LoginFragment в FragmentActivity, тому для того, щоб це працювало, вам потрібно додати HomeFragment через FragmentActivity, коли вхід закриється.

Загальним моментом є те, що вам потрібен клас FragmentActivity, з якого ви додаєте кожен фрагмент до FragmentManager. Це неможливо зробити всередині класу Fragment.


Так, вони всередині фрагмента діяльності.
— Крішнабхадра

якщо фрагменти додаються динамічно, ви можете додати фрагмент стільки, скільки вам потрібно, але не до тих, що визначені у тезі xml <fragment>
— Незаконне аргументування

1

Якщо ви додасте фрагмент у XML, ви не зможете динамічно поміняти їх. Що трапляється, це вони надмірно, тому вони не розгортаються так, як можна було б очікувати. Проблема задокументована в цьому питанні. FragmenManager замінює робить накладення

Перетворіть middle_fragment в FrameLayout і завантажте його, як нижче, і ваші події розпочнуться.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();

1

Ви можете спробувати це,

Крок 1: Переосмислить вибраний метод табсера у вашій діяльності

@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    try {
    if(MyEventsFragment!=null && tab.getPosition()==3)
    {
        MyEvents.fragmentChanged();
    }
    }
    catch (Exception e)
    {

    }
    mViewPager.setCurrentItem(tab.getPosition());
}

Крок 2. Використовуючи статичний метод, виконайте те, що ви хочете у своєму фрагменті,

public static void fragmentChanged()
{
    Toast.makeText(actvity, "Fragment Changed", Toast.LENGTH_SHORT).show();
}

1

На основі відповіді @Gor я написав подібне в Котліні. Помістіть цей код у onCreate()діяльності. Працює на видимий один фрагмент. Якщо у вас є ViewPagerфрагменти, він буде викликати ViewPagerфрагмент, а не попередній.

supportFragmentManager.addOnBackStackChangedListener {
    supportFragmentManager.fragments.lastOrNull()?.onResume()
}

Прочитавши https://medium.com/@elye.project/puzzle-fragment-stack-pop- why- issue- on- toolbar- 8b947c5c07c6, я зрозумів, що в багатьох ситуаціях краще приєднати нові фрагменти replace, а не add. Тож потреба в onResumeдеяких випадках зникне.


1

Я використовую у своїй діяльності - КОТЛІН

supportFragmentManager.addOnBackStackChangedListener {
                val f = supportFragmentManager.findFragmentById(R.id.fragment_container)

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }

0

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


0

onPause() працює в класі діяльності, який ви можете використовувати:

public void onDestroyView(){
super.onDestroyView    
}

з цією ж метою ..


0

Виконайте наведені нижче дії, і ви отримаєте необхідну відповідь

1- Для обох фрагментів створіть новий абстрактний батьківський.
2- Додайте спеціальний абстрактний метод, який слід реалізувати обом.
3- Викличте його від поточного екземпляра перед тим, як замінити другий.


Як це допомагає в ситуації, коли користувач повертається з фрагмента 2 до фрагмента 1?
— CoolMind

0

Виклик ft.replaceповинен викликати OnPause (заміненого фрагмента) та onResume (замінюючого фрагмента).

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


Це не відповідь. Що робити, якщо "додати" обов'язково для чийогось дизайну ?!
— Фарид

0

Хоча з різним кодом, я відчував ту ж проблему, що і ОП, тому що спочатку використовував

fm.beginTransaction()
            .add(R.id.fragment_container_main, fragment)
            .addToBackStack(null)
            .commit();

замість

fm.beginTransaction()
                .replace(R.id.fragment_container_main, fragment)
                .addToBackStack(null)
                .commit();

При "заміні" перший фрагмент відтворюється, коли ви повертаєтесь з другого фрагмента, і тому onResume () також викликається.


Це не відповідь. Що робити, якщо "додати" обов'язково для чийогось дизайну ?!
— Фарид

-2

Створюючи транзакцію з фрагментами, обов’язково додайте наступний код.

// Replace whatever is in the fragment_container view with this fragment, 
// and add the transaction to the back stack 
transaction.replace(R.id.fragment_container, newFragment); 
transaction.addToBackStack(null); 

Також переконайтеся, що здійснити транзакцію після додавання її в backstack

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.