O fragmento onResume () & onPause () não é chamado no backstack

194

Eu tenho vários fragmentos dentro de uma atividade. Em um botão, estou iniciando um novo fragmento, adicionando-o ao backstack. Eu naturalmente esperava que o onPause()método do fragmento atual e onResume()do novo fragmento fosse chamado. Bem, isso não está acontecendo.

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();
  }
}

O que eu esperava era

  1. Quando o botão é clicado, o LoginFragment é substituído por HomeFragment , onPause()de LoginFragment e onResume()de HomeFragment chamado
  2. Quando volta é pressionado, HomeFragment é poped fora e LoginFragment é visto, e onPause()de HomeFragment e onResume()de LoginFragment é chamado.

O que estou recebendo é,

  1. Quando o botão é clicado, o HomeFragment está substituindo corretamente o LoginFragment , onResume () do HomeFragment é chamado, mas a onPause () do LoginFragment nunca é chamada.
  2. Quando pressionado novamente, o HomeFragment aparece corretamente para revelar o LoginFragment , a onPause () do HomeFragment é chamada, mas o onResume () do LoginFragment nunca é chamado.

Esse é o comportamento normal? Por que é onResume()de LoginFragment não ser chamado quando pressiono o botão de volta.

Krishnabhadra
fonte
Adicione o código de atividade que lida com os fragmentos.
22412 blessenm
eu estou tendo o problema da amostra, em pausa não ser chamado, como você resolver isso,
Sam
Eu tive o mesmo problema, mas percebi que estava usando ft2.add (); em vez de ft2.replace (). Apenas outra razão seria se sua atividade está mantendo uma referência ao fragmento (adicionando-o a uma coleção, ou atribuí-la a uma variável de classe)
Friggles
3
Eu estou tendo o mesmo problema. Notei que .replace () chamará os métodos necessários do ciclo de vida, mas essencialmente destrói o fragmento. Além disso, onSaveInstanceState não é chamado. Como tal, não posso manter seu estado. Então, eu preciso usar add, mas o onResume / Pausa não é chamado :(
ariets
FWIW, minha experiência é que os fragmentos da biblioteca de suporte chamam onPause e onResume ao pressionar / abrir o backstack, mas os fragmentos internos do Android não. Ainda não encontrou uma solução adequada para isso.
benkc

Respostas:

185

Os fragmentos onResume()ou onPause()serão chamados apenas quando as Atividades onResume()ou onPause()forem chamados. Eles estão fortemente acoplados ao Activity.

Leia a seção Manipulando o ciclo de vida do fragmento deste artigo .

Sagar Waghmare
fonte
1
No artigo, é dito que "quando a atividade atingir o estado retomado, você poderá adicionar e remover livremente fragmentos da atividade. Portanto, somente enquanto a atividade estiver no estado retomado, o ciclo de vida de um fragmento será alterado independentemente". Isso significa que o fragmento onResume pode ser chamado mesmo que a atividade onResume não seja chamada?
v4r 27/05
3
quanto aos fragmentos nativos (não suportados) no 4.4 (não tenho certeza se isso é verdadeiro para versões mais antigas), onPause () e onResume () são chamados não apenas quando esses eventos ocorrem na atividade, mas, por exemplo, quando você chama replace () ou adiciona () / remove () durante a transação, portanto, esta resposta é enganosa, pelo menos para as versões recentes do Android.
Dmide
19
De acordo com esse documento, o fragmento deve realmente ser movido para o estado parado quando trocado para o backstack. Mas não são apenas onPause e onResume que não são chamados, nem onStop e onStart - ou, nesse caso, quaisquer outros métodos de ciclo de vida. Portanto, o guia é definitivamente enganoso.
benkc
1
Embora não esteja relacionado, mas encontrei esta pergunta ao procurar pelo meu problema de onPause()ser chamado depois e onSaveInstanceState()não antes. Isso pode ser reproduzida se a uma criança diferente na minha FragmentStatePagerAdapter(dizer que você passar de criança 0 a criança 2, nota para si mesmo, isso acontece porque criança 0 é destruída quando criança 2 abre )
Sufian
Não tenho certeza do que você quer dizer. Chamar ft.replace deve acionar onPause (do fragmento substituído) e onResume (do fragmento de substituição). Isso é feito independentemente de qualquer atividade ...
David Refaeli
20
  • Desde que você usou ft2.replace(), o FragmentTransaction.remove() método é chamado e o Loginfragmentserá removido. Consulte isso . Então, onStop()de LoginFragmentserá chamado em vez de onPause(). (Como o novo fragmento substitui completamente o antigo).
  • Mas como você também usou ft2.addtobackstack(), o estado do Loginfragmentserá salvo como um pacote e, quando você clicar no botão Voltar de HomeFragment, onViewStateRestored()será chamado seguido por onStart()de LoginFragment. Então, eventualmente onResume(), não será chamado.
Gopal
fonte
1
onViewStateRestoredé chamado se você tiversetRetainInstance(true)
Farid
14

Aqui está a minha versão mais robusta da resposta de Gor (o uso de fragments.size () não é confiável devido ao tamanho não ser diminuído após o fragmento ser populado)

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. 
                    }
                }
            }
        }
    });

E o método '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
fonte
9

Se você realmente deseja substituir fragmentos dentro de outro fragmento, use Fragmentos Aninhados .

No seu código, você deve substituir

final FragmentManager mFragmentmanager =  getFragmentManager();

com

final FragmentManager mFragmentmanager =  getChildFragmentManager();
Bersh
fonte
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
            }

        }
    });

mas tenha cuidado se mais de um fragmento visível

Gor
fonte
Obrigado, funciona, mas se apenas um fragmento estiver visível (os ViewPagerfragmentos farão referência aos seus fragmentos).
CoolMind 02/08
3

Eu tenho um código muito semelhante ao seu e se ele funciona em onPause () e onResume (). Ao alterar o fragmento, essas funções são ativadas respectivamente.

Código no fragmento:

 @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");

}

Registre quando a alteração do fragmento:

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

Fragmento 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;
}

Ação quando pulso de volta (na atividade em que carrego o fragmento):

@Override
public void onBackPressed() {

    int count = getFragmentManager().getBackStackEntryCount();

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

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

 }
PepitoGrillo
fonte
2

O que eu faço no fragmento filho:

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

E, em seguida, substitua onResume em ParentFragment

Guillermo De Juan
fonte
1
Você nunca deve chamar métodos de ciclo de vida manualmente, especialmente um dentro do outro
breakline
@breakline esta técnica funciona. Você tem outra maneira?
Vikash Parajuli 6/0318
Sim, você deve adicionar sua própria implementação para chamar, porque os métodos de ciclo de vida também são chamados pelo sistema e, se você chamar métodos de ciclo de vida um dentro do outro como este, poderá (e provavelmente causará) problemas posteriores.
breakline
1

Você simplesmente não pode adicionar um fragmento a um fragmento. Isso precisa acontecer no FragmentActivity. Suponho que você esteja criando o LoginFragment em um FragmentActivity, portanto, para que isso funcione, é necessário adicionar o HomeFragment por meio do FragmentActivity quando o login for fechado.

O ponto geral é que você precisa de uma classe FragmentActivity de onde você adiciona cada fragmento ao FragmentManager. Não é possível fazer isso dentro de uma classe Fragment.

Nickolaus
fonte
Sim, eles estão dentro de uma atividade de fragmento.
Krishnabhadra
se fragmentos são adicionados dinamicamente, então você pode adicionar quantos fragmento como você quer um fragmento, mas não os definidos no xml <fragmento> tag
Argumento Ilegal
1

Se você adicionar o fragmento em XML, não poderá trocá-los dinamicamente. O que acontece é que eles são excessivamente, para que os eventos não disparem como seria de esperar. O problema está documentado nesta pergunta. Substituição FragmenManager faz sobreposição

Transforme middle_fragment em um FrameLayout e carregue-o como abaixo e seus eventos serão acionados.

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

Você pode tentar isso,

Etapa 1: substituir o método Tabselected em sua atividade

@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());
}

Etapa 2: usando o método estático, faça o que você deseja no seu fragmento,

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

Eu uso na minha atividade - KOTLIN

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

                if (f?.tag == "MyFragment")
                {
                    //doSomething
                }
            }
Álvaro Agüero
fonte
0

Um fragmento sempre deve ser incorporado a uma atividade e o ciclo de vida do fragmento é diretamente afetado pelo ciclo de vida da atividade do host. Por exemplo, quando a atividade está em pausa, todos os fragmentos estão nela e, quando a atividade é destruída, todos os fragmentos também.

Zar E Ahmer
fonte
0

onPause() O método funciona na classe de atividade que você pode usar:

public void onDestroyView(){
super.onDestroyView    
}

para a mesma finalidade ..

Vikrant Kaloniya
fonte
0

Siga as etapas abaixo e obterá a resposta necessária

1- Para ambos os fragmentos, crie um novo pai abstrato.
2- Adicione um método abstrato personalizado que deve ser implementado por ambos.
3- Chame a partir da instância atual antes de substituir pela segunda.

Mostafa Magdy
fonte
Como ajuda na situação em que um usuário retorna do fragmento 2 para o fragmento 1?
CoolMind
0

Com base na resposta de @Gor , escrevi semelhante em Kotlin. Coloque esse código em onCreate()uma atividade. Funciona para um fragmento visível. Se você tiver ViewPagerfragmentos, ele chamará ViewPagero fragmento, não o anterior.

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

Depois de ler https://medium.com/@elye.project/puzzle-fragment-stack-pop-cause-issue-on-toolbar-8b947c5c07c6 , entendi que seria melhor em muitas situações anexar novos fragmentos replace, não add. Portanto, onResumeem alguns casos, a necessidade desaparecerá.

CoolMind
fonte
0

Chamando ft.replace deve acionar onPause (do fragmento substituído) e onResume (do fragmento de substituição).

Percebo que seu código é inflado login_fragmentno fragmento de origem e também não retorna as visualizações no onCreateView. Se estes são erros de digitação, você pode mostrar como esses fragmentos estão sendo chamados de dentro da sua atividade?

David Refaeli
fonte
Isso não é uma resposta, e se "adicionar" for obrigatório para o design de alguém ?!
Farid
0

Embora com código diferente, tive o mesmo problema do OP, porque originalmente usei

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

ao invés de

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

Com "substituir", o primeiro fragmento é recriado quando você retorna do segundo fragmento e, portanto, onResume () também é chamado.

Greg Holst
fonte
Isso não é uma resposta, e se "adicionar" for obrigatório para o design de alguém ?!
Farid
-2

Ao criar uma transação de fragmento, adicione o seguinte código.

// 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); 

Certifique-se também de confirmar a transação após adicioná-la ao backstack

Mayur Mais
fonte