Фрагмент 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 не вызывался , когда я нажимаю кнопку назад.

Krishnabhadra
источник
Добавьте код активности, который обрабатывает фрагменты.
blessenm
у меня проблема с образцом, на паузу не позвонили, как вы решили это,
Сэм
У меня была та же проблема, но я понял, что использую ft2.add (); вместо ft2.replace (). Единственная другая причина может быть, если ваша деятельность хранит ссылку на фрагмент (добавляя ее в коллекцию или присваивая ее переменной класса)
Friggles
3
У меня та же проблема. Я заметил, что .replace () будет вызывать необходимые методы жизненного цикла, но по сути уничтожает фрагмент. Также onSaveInstanceState не вызывается . Поэтому я не могу сохранить его состояние. Итак, мне нужно использовать add, но onResume / Pause не вызывается :(
ariets
FWIW, мой опыт показывает, что фрагменты библиотеки поддержки вызывают onPause и onResume при нажатии / отбрасывании backstack, но встроенные фрагменты Android этого не делают. Не нашли подходящего обходного пути для этого.
benkc

Ответы:

187

Фрагменты onResume()или onPause()будут называться только тогда, когда деятельность onResume()или onPause()называется. Они тесно связаны с Activity.

Прочитайте раздел «Обработка жизненного цикла фрагмента» этой статьи .

Сагар Вагмар
источник
1
В статье говорится, что «как только действие достигает возобновленного состояния, вы можете свободно добавлять и удалять фрагменты к действию. Таким образом, жизненный цикл фрагмента может изменяться независимо только в том случае, если действие находится в возобновленном состоянии». Означает ли это, что фрагмент onResume может быть вызван, даже если не вызывается активность onResume?
v4r
3
что касается нативных (не поддерживаемых) фрагментов в 4.4 (не уверен, верно ли это для более старых версий) onPause () и onResume () вызываются не только тогда, когда эти события происходят в действии, но, например, когда вы вызываете replace () или add () / remove () во время выполнения транзакции, поэтому этот ответ вводит в заблуждение по крайней мере для последних версий Android.
августа
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;
}
aaronmarino
источник
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
            }

        }
    });

но будьте осторожны, если видно более одного фрагмента

Gor
источник
Спасибо, это работает, но если виден только один фрагмент (так, ViewPagerс фрагментами будет ссылаться на его фрагменты).
CoolMind
3

У меня есть код, очень похожий на ваш, и если он работает onPause () и 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();
    }

 }
PepitoGrillo
источник
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.

Nickolaus
источник
Да, они внутри фрагмента деятельности.
Кришнабхадра
если фрагменты добавляются динамически, то вы можете добавить столько фрагментов, сколько захотите, к фрагменту, но не к тем, которые определены в теге <ml> xml
Illegal Argument
1

Если вы добавите фрагмент в XML, вы не сможете поменять их динамически. То, что происходит, они чрезмерно, так что события не происходят, как можно было бы ожидать. Проблема задокументирована в этом вопросе.FragmenManager заменяет делает оверлей

Превратите middle_fragment в FrameLayout и загрузите его, как показано ниже, и ваши события сработают.

getFragmentManager().beginTransation().
    add(R.id.middle_fragment, new MiddleFragment()).commit();
Dustin
источник
1

Вы можете попробовать это,

Шаг 1: переопределите метод Tabselected в вашей деятельности

@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 я написал подобное в Kotlin. Поместите этот код в onCreate()деятельности. Это работает для одного видимого фрагмента. Если у вас есть ViewPagerфрагменты, он будет называть ViewPagerфрагмент, а не предыдущий.

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

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

CoolMind
источник
1

Я использую в своей деятельности - KOTLIN

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

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
Альваро Агуэро
источник
0

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

Zar E Ahmer
источник
0

onPause() Метод работает в классе деятельности вы можете использовать:

public void onDestroyView(){
super.onDestroyView    
}

для той же цели ..

Викрант Калония
источник
0

Выполните следующие шаги, и вы получите нужный ответ

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

Мостафа Магди
источник
Как это помогает в ситуации, когда пользователь возвращается из фрагмента 2 во фрагмент 1?
CoolMind
0

призвание ft.replace должен вызывать onPause (замещенного фрагмента) и onResume (замещающего фрагмента).

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

Давид Рафаэли
источник
Это не ответ, что, если «добавить» является обязательным для чьего-то дизайна ?!
Фарид
0

Хотя с другим кодом я столкнулся с той же проблемой, что и OP, потому что изначально использовал

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.

Mayur More
источник