Este é o cenário: Atividade contém fragmento A
, que por sua vez usa getChildFragmentManager()
para adicionar fragmentos A1
e A2
da onCreate
mesma forma:
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
Até agora, tudo bem, tudo está funcionando conforme o esperado.
Em seguida, executamos a seguinte transação na atividade:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(anim1, anim2, anim1, anim2)
.replace(R.id.fragmentHolder, new FragmentB())
.addToBackStack(null)
.commit()
Durante a transição, as enter
animações do fragmento são B
executadas corretamente, mas os fragmentos A1 e A2 desaparecem completamente . Quando revertemos a transação com o botão Voltar, eles inicializam corretamente e são exibidos normalmente durante a popEnter
animação.
Em meu breve teste, ficou mais estranho - se eu definir as animações para os fragmentos filhos (veja abaixo), a exit
animação é executada de forma intermitente quando adicionamos o fragmentoB
getChildFragmentManager()
.beginTransaction()
.setCustomAnimations(enter, exit)
.replace(R.id.fragmentOneHolder, new FragmentA1())
.replace(R.id.fragmentTwoHolder, new FragmentA2())
.commit()
O efeito que desejo alcançar é simples - desejo exit
(ou deveria ser popExit
?) A animação no fragmento A
(anim2) para ser executada, animando todo o contêiner, incluindo seus filhos aninhados.
Existe alguma maneira de conseguir isso?
Editar : Encontre um caso de teste aqui
Edit2 : Obrigado a @StevenByle por me empurrar para continuar tentando com as animações estáticas. Aparentemente, você pode definir animações por operação (não global para toda a transação), o que significa que os filhos podem ter um conjunto de animações estáticas indefinidas, enquanto seus pais podem ter uma animação diferente e tudo pode ser confirmado em uma transação . Veja a discussão abaixo e o projeto de caso de teste atualizado .
R.id.fragmentHolder
em relação a A, A1, A2, etc?changeFragment
método apenas uma vez?Respostas:
Para evitar que o usuário veja os fragmentos aninhados desaparecendo quando o fragmento pai é removido / substituído em uma transação, você pode "simular" esses fragmentos ainda presentes fornecendo uma imagem deles, conforme apareceram na tela. Essa imagem será usada como plano de fundo para o contêiner de fragmentos aninhados, portanto, mesmo que as visualizações do fragmento aninhado desapareçam, a imagem simulará sua presença. Além disso, não vejo perder a interatividade com as visualizações do fragmento aninhado como um problema, porque não acho que você gostaria que o usuário agisse sobre eles quando estão apenas em processo de remoção (provavelmente como uma ação do usuário como bem).
Fiz um pequeno exemplo com a configuração da imagem de fundo (algo básico).
fonte
Portanto, parece haver várias soluções alternativas para isso, mas com base na resposta de @ Jayd16, acho que encontrei uma solução abrangente que ainda permite animações de transição personalizadas em fragmentos filhos, e não exige um cache de bitmap do layout.
Tenha uma
BaseFragment
classe que estendaFragment
e faça com que todos os seus fragmentos estendam essa classe (não apenas os fragmentos filhos).Nessa
BaseFragment
classe, adicione o seguinte:Infelizmente, requer reflexão; entretanto, como esta solução alternativa é para a biblioteca de suporte, você não corre o risco de a implementação subjacente mudar, a menos que atualize sua biblioteca de suporte. Se você estiver construindo a biblioteca de suporte a partir da fonte, poderá adicionar um acessador para a próxima ID de recurso de animação
Fragment.java
e remover a necessidade de reflexão.Esta solução remove a necessidade de "adivinhar" a duração da animação do pai (de modo que a animação "não fazer nada" tenha a mesma duração da animação de saída do pai) e permite que você ainda faça animações personalizadas em fragmentos filhos (por exemplo, se você ' re trocando fragmentos filhos com diferentes animações).
fonte
mNextAnim
agora está dentro de ummAnimationInfo
objeto. Você pode acessá-lo assim:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo");
animInfoField.setAccessible(true);
Object animationInfo = animInfoField.get(fragment);
Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
val nextAnimResource = nextAnimField.getInt(animationInfo);
para substituir a linhaint nextAnimResource = nextAnimField.getInt(fragment);
Consegui chegar a uma solução bastante limpa. IMO é o menos hacky, e embora esta seja tecnicamente a solução "desenhar um bitmap", pelo menos é abstraído pelo fragmento lib.
Certifique-se de que seu filho frags substitua uma classe pai com isto:
Se tivermos uma animação de saída nos fragmentos filhos, eles serão animados em vez de piscar. Podemos explorar isso tendo uma animação que simplesmente desenha os fragmentos filhos em alfa total por um período. Dessa forma, eles ficarão visíveis no fragmento pai conforme ele se anima, dando o comportamento desejado.
O único problema em que consigo pensar é em manter o controle dessa duração. Eu poderia definir um número grande, mas temo que possa haver problemas de desempenho se ainda estiver desenhando a animação em algum lugar.
fonte
Estou postando minha solução para maior clareza. A solução é bastante simples. Se você está tentando imitar a animação da transação do fragmento do pai, simplesmente adicione uma animação personalizada à transação do fragmento do filho com a mesma duração. Ah, e certifique-se de definir a animação personalizada antes de add ().
O xml para R.anim.none (o tempo de animação de meus pais entram / saem é 250ms)
fonte
fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
Eu entendo que isso pode não ser capaz de resolver completamente o seu problema, mas talvez seja adequado para as necessidades de outra pessoa, você pode adicionar
enter
/exit
epopEnter
/popExit
animações aos seus filhosFragment
que não movem / animam os programasFragment
. Contanto que as animações tenham a mesma duração / deslocamento que suasFragment
animações principais , elas parecerão se mover / animar com a animação dos pais.fonte
você pode fazer isso no fragmento filho.
fonte
@@@@@@@@@@@@@@@@@@@@@@@@@@@@
EDIT: Acabei não implementando esta solução porque havia outros problemas que isso tem. O Square lançou recentemente 2 bibliotecas que substituem fragmentos. Eu diria que essa pode ser uma alternativa melhor do que tentar hackear fragmentos para fazer algo que o Google não quer que eles façam.
http://corner.squareup.com/2014/01/mortar-and-flow.html
@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Achei que deveria apresentar essa solução para ajudar as pessoas que terão esse problema no futuro. Se você acompanhar a conversa do pôster original com outras pessoas e olhar o código que ele postou, verá que o pôster original eventualmente chega à conclusão de usar uma animação autônoma nos fragmentos filhos enquanto anima o fragmento pai. Esta solução não é ideal, pois força você a manter o controle de todos os fragmentos filhos, o que pode ser complicado ao usar um ViewPager com FragmentPagerAdapter.
Como eu uso fragmentos de criança em todos os lugares, criei essa solução que é eficiente e modular (para que possa ser facilmente removido) caso eles consigam consertar e essa animação autônoma não seja mais necessária.
Existem muitas maneiras de implementar isso. Escolhi usar um singleton e o chamo de ChildFragmentAnimationManager. Basicamente, ele acompanhará um fragmento filho para mim com base em seu pai e aplicará uma animação sem operação aos filhos quando solicitado.
Em seguida, você precisa ter uma classe que estende Fragment que todos os seus Fragments estendem (pelo menos seus Fragmentos Filhos). Eu já tive essa classe e a chamo de BaseFragment. Quando uma visualização de fragmentos é criada, nós a adicionamos ao ChildFragmentAnimationManager e a removemos quando é destruída. Você pode fazer isso em Attach / Detach ou em outros métodos de correspondência na sequência. Minha lógica para escolher Create / Destroy View foi porque se um Fragment não tem uma View, eu não me importo em animá-lo para continuar a ser visto. Essa abordagem também deve funcionar melhor com ViewPagers que usam Fragments, já que você não acompanhará cada Fragment que um FragmentPagerAdapter está mantendo, mas apenas 3.
Agora que todos os seus fragmentos estão armazenados na memória pelo fragmento pai, você pode chamar animate neles assim, e seus fragmentos filhos não desaparecerão.
Além disso, para que você tenha, aqui está o arquivo no_anim.xml que vai para sua pasta res / anim:
Novamente, não acho que essa solução seja perfeita, mas é muito melhor do que para cada instância que você tem um fragmento filho, implementando código personalizado no fragmento pai para manter o controle de cada filho. Eu estive lá e não é divertido.
fonte
Acho que encontrei uma solução melhor para esse problema do que capturar o fragmento atual em um bitmap, como sugeriu Luksprog.
O truque é se esconder o fragmento que está sendo removido ou destacado e somente após as animações terem sido concluídas o fragmento é removido ou destacado em sua própria transação de fragmento.
Imagine que temos
FragmentA
eFragmentB
, ambos com fragmentos. Agora, quando você normalmente faria:Ao invés você faz
Agora, para a implementação do Fragmento:
fonte
Eu estava tendo o mesmo problema com o fragmento do mapa. Ele continuou desaparecendo durante a animação de saída do fragmento que o continha. A solução alternativa é adicionar animação para o fragmento do mapa filho que o manterá visível durante a animação de saída do fragmento pai. A animação do fragmento filho está mantendo seu alfa em 100% durante o período de duração.
Animação: res / animator / keep_child_fragment.xml
A animação é então aplicada quando o fragmento do mapa é adicionado ao fragmento pai.
Fragmento pai
Finalmente, a duração da animação do fragmento filho é definida em um arquivo de recurso.
valores / integers.xml
fonte
Para animar o desaparecimento de fragmentos aninhados, podemos forçar a pilha de retorno pop em ChildFragmentManager. Isso irá disparar a animação de transição. Para fazer isso, precisamos acompanhar o evento OnBackButtonPressed ou ouvir as alterações de backstack.
Aqui está um exemplo com código.
fonte
Recentemente, encontrei este problema na minha pergunta: fragmentos aninhados em transição incorreta
Tenho uma solução que resolve isso sem salvar um bitmap, nem usar reflexão ou qualquer outro método insatisfatório.
Um exemplo de projeto pode ser visto aqui: https://github.com/zafrani/NestedFragmentTransitions
Um GIF do efeito pode ser visto aqui: https://imgur.com/94AvrW4
No meu exemplo, existem 6 fragmentos filhos, divididos entre dois fragmentos pais. Consigo realizar as transições para entrar, sair, pop e push sem problemas. Mudanças de configuração e impressão traseira também são tratadas com sucesso.
A maior parte da solução está na função onCreateAnimator do meu BaseFragment (o fragmento estendido por meus filhos e fragmentos pais), que se parece com isto:
A atividade e o fragmento pai são responsáveis por definir os estados desses booleanos. É mais fácil ver como e onde no meu projeto de exemplo.
Não estou usando fragmentos de suporte em meu exemplo, mas a mesma lógica pode ser usada com eles e sua função onCreateAnimation
fonte
Uma maneira simples de corrigir esse problema é usar a
Fragment
classe desta biblioteca em vez da classe de fragmento de biblioteca padrão:https://github.com/marksalpeter/contract-fragment
Como uma observação lateral, o pacote também contém um padrão de delegado útil chamado
ContractFragment
que você pode achar útil para construir seus aplicativos aproveitando a relação de fragmento pai-filho.fonte
Da resposta acima de @kcoppock,
se você tiver Atividade-> Fragmento-> Fragmentos (empilhamento múltiplo, o seguinte ajuda), uma pequena edição para a melhor resposta IMHO.
fonte
Meu problema era na remoção do fragmento pai (ft.remove (fragmento)), as animações filho não estavam acontecendo.
O problema básico é que os fragmentos dos filhos são imediatamente DESTRUÍDOS ANTES do fragmento dos pais que sai da animação.
As animações personalizadas de fragmentos filhos não são executadas na remoção do fragmento pai
Como outras pessoas não conseguiram, esconder o PAI (e não a criança) antes da remoção do PAI é o caminho a percorrer.
Se você realmente deseja remover o pai, você provavelmente deve configurar um ouvinte em sua animação personalizada para saber quando a animação termina, então você pode fazer alguma finalização com segurança no fragmento pai (remover). Se você não fizer isso, em tempo hábil, você pode acabar matando a animação. A animação NB é feita em fila assíncrona própria.
BTW, você não precisa de animações personalizadas no fragmento filho, pois elas herdarão as animações pai.
fonte
O problema foi corrigido em
androidx.fragment:fragment:1.2.0-alpha02
. Consulte https://issuetracker.google.com/issues/116675313 para obter mais detalhes.fonte
Tópico antigo, mas no caso de alguém tropeçar aqui:
Todas as abordagens acima parecem muito desagradáveis para mim, a solução de bitmap é muito suja e sem desempenho; os outros requerem que os fragmentos filho saibam sobre a duração da transição usada na transação usada para criar o fragmento filho em questão. A melhor solução aos meus olhos é algo como o seguinte:
Apenas escondemos o fragmento atual e adicionamos o novo fragmento, quando a animação terminar removemos o fragmento antigo. Dessa forma, ele é tratado em um lugar e nenhum bitmap é criado.
fonte