Фрагмент 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.