Fragmentos aninhados desaparecem durante a animação de transição

100

Este é o cenário: Atividade contém fragmento A, que por sua vez usa getChildFragmentManager()para adicionar fragmentos A1e A2da onCreatemesma 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 enteranimações do fragmento são Bexecutadas 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 popEnteranimação.

Em meu breve teste, ficou mais estranho - se eu definir as animações para os fragmentos filhos (veja abaixo), a exitanimaçã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 .

Delyan
fonte
O que é R.id.fragmentHolderem relação a A, A1, A2, etc?
CommonsWare de
fragmentHolder é um id no layout da atividade, fragmento {One, Two} Holder está no layout do fragmento A. Todos os três são distintos. O fragmento A foi inicialmente adicionado em fragmentHolder (ou seja, o fragmento B está substituindo o fragmento A).
Delyan de
Criei um projeto de amostra aqui: github.com/BurntBrunch/NestedFragmentsAnimationsTest , também há um apk incluído no repositório. Este é um bug realmente irritante e estou procurando uma maneira de contorná-lo (presumindo que não esteja no meu código).
Delyan
Eu sei um pouco mais sobre esse problema agora. A razão pela qual os fragmentos desaparecem é porque os filhos lidam com os eventos do ciclo de vida antes dos pais. Em essência, A1 e A2 são removidos antes de A e, como não têm animações definidas, desaparecem abruptamente. Uma maneira de atenuar um pouco isso é remover explicitamente A1 e A2 na transação que substitui A. Dessa forma, eles se animam ao sair, no entanto, sua velocidade de animação é elevada ao quadrado, já que o contêiner pai também está animando. Uma solução que não produza este artefato seria apreciada.
Delyan
A alteração (substituindo o fragmento inicial) que você menciona na pergunta é a real que você deseja fazer ou é apenas um exemplo? Você chamará o changeFragmentmétodo apenas uma vez?
Luksprog de

Respostas:

36

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

Luksprog
fonte
1
Resolvi premiar você com a recompensa, já que foi essa a solução que acabei usando. Muito obrigado pelo seu tempo!
Delyan
16
Uau, tão imundo. Até onde nós, desenvolvedores de Android, temos que ir apenas por alguma habilidade
Dean Wild,
1
Tenho um Viewpager da segunda guia, estou substituindo outro fragmento e, quando pressiono de volta, preciso mostrar a segunda guia do viewpager, ela está abrindo, mas mostra uma página em branco. Eu tentei o que você sugeriu no tópico acima, mas ainda é o mesmo.
Harish
O problema permanece quando o usuário volta como @Harish escreveu
Ewoks
1
Uau, sério? é 2018 e isso ainda é uma coisa? :(
Archie G. Quiñones
69

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 BaseFragmentclasse que estenda Fragmente faça com que todos os seus fragmentos estendam essa classe (não apenas os fragmentos filhos).

Nessa BaseFragmentclasse, adicione o seguinte:

// Arbitrary value; set it to some reasonable default
private static final int DEFAULT_CHILD_ANIMATION_DURATION = 250;

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    final Fragment parent = getParentFragment();

    // Apply the workaround only if this is a child fragment, and the parent
    // is being removed.
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}

private static long getNextAnimationDuration(Fragment fragment, long defValue) {
    try {
        // Attempt to get the resource ID of the next animation that
        // will be applied to the given fragment.
        Field nextAnimField = Fragment.class.getDeclaredField("mNextAnim");
        nextAnimField.setAccessible(true);
        int nextAnimResource = nextAnimField.getInt(fragment);
        Animation nextAnim = AnimationUtils.loadAnimation(fragment.getActivity(), nextAnimResource);

        // ...and if it can be loaded, return that animation's duration
        return (nextAnim == null) ? defValue : nextAnim.getDuration();
    } catch (NoSuchFieldException|IllegalAccessException|Resources.NotFoundException ex) {
        Log.w(TAG, "Unable to load next animation from parent.", ex);
        return defValue;
    }
}

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.javae 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).

Kevin Coppock
fonte
5
Esta é minha solução favorita no tópico. Não requer um bitmap, não requer nenhum código extra no fragmento filho e realmente não vaza informações do pai para o fragmento filho.
jacobhyphenated
1
@EugenPechanec Você precisa do nextAnim do fragmento pai - não do filho. Esse é o ponto principal.
Kevin Coppock,
1
Infelizmente, essa abordagem causa um vazamento de memória para os fragmentos filhos e implicitamente para o fragmento pai, bem como nas versões do Android abaixo do Lollipop :(
Cosmin
8
Obrigado por esta solução muito útil, porém requer uma pequena atualização para funcionar com a biblioteca de suporte atual (27.0.2, não sei qual versão quebrou este código). mNextAnimagora está dentro de um mAnimationInfoobjeto. 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");
David Lericolais
5
@DavidLericolais, gostaria de adicionar mais uma linha de código após a sua. val nextAnimResource = nextAnimField.getInt(animationInfo);para substituir a linhaint nextAnimResource = nextAnimField.getInt(fragment);
tingyik90
32

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:

private static final Animation dummyAnimation = new AlphaAnimation(1,1);
static{
    dummyAnimation.setDuration(500);
}

@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
    if(!enter && getParentFragment() != null){
        return dummyAnimation;
    }
    return super.onCreateAnimation(transit, enter, nextAnim);
}

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.

Jayd16
fonte
Isso ajuda, obrigado. O valor do tempo de duração não importa
iscariotes
solução mais limpa até agora
Liran Cohen
16

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 ().

getChildFragmentManager().beginTransaction()
        .setCustomAnimations(R.anim.none, R.anim.none, R.anim.none, R.anim.none)
        .add(R.id.container, nestedFragment)
        .commit();

O xml para R.anim.none (o tempo de animação de meus pais entram / saem é 250ms)

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="0" android:duration="250" />
</set>
Maurycy
fonte
Fiz algo muito semelhante, mas usei "mostrar" em vez de "adicionar" ao atualizar o filho. Também adicionei "getChildFragmentManager (). ExecutePendingTransactions ()", embora não tenha certeza se isso é estritamente necessário. Esta solução funciona bem, porém, e não requer "fornecer uma imagem" do Fragmento como alguns sugerem.
Brian Yencho
Isso foi ótimo. No entanto, eu estava percebendo o atraso ao trocar meus fragmentos de filho. para evitar isso, basta definir o segundo parâmetro sem anim:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
ono
7

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/ exite popEnter/ popExitanimações aos seus filhos Fragmentque não movem / animam os programas Fragment. Contanto que as animações tenham a mesma duração / deslocamento que suas Fragmentanimações principais , elas parecerão se mover / animar com a animação dos pais.

Steven Byle
fonte
1
Eu concedi a recompensa a Luksprog, já que sua solução funciona universalmente. Eu tentei o truque das animações estáticas (a duração realmente não importa - uma vez que o pai se foi, as visualizações obviamente desaparecem), mas elas não funcionaram em todos os casos possíveis (veja meus comentários na pergunta). Além disso, essa abordagem vaza abstração, uma vez que o pai de um fragmento com filhos precisa saber sobre esse fato e tomar medidas extras para definir as animações dos filhos. Em qualquer caso, muito obrigado pelo seu tempo!
Delyan
Concordo, esta é mais uma solução alternativa do que uma solução estanque e pode ser considerada um pouco frágil. Mas funcionará para casos simples.
Steven Byle
4

você pode fazer isso no fragmento filho.

@Override
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
    if (true) {//condition
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(getView(), "alpha", 1, 1);
        objectAnimator.setDuration(333);//time same with parent fragment's animation
        return objectAnimator;
    }
    return super.onCreateAnimator(transit, enter, nextAnim);
}
peng gao
fonte
Obrigado! Talvez não seja a melhor, mas possivelmente a solução mais simples.
bug56
2

@@@@@@@@@@@@@@@@@@@@@@@@@@@@

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.

public class ChildFragmentAnimationManager {

private static ChildFragmentAnimationManager instance = null;

private Map<Fragment, List<Fragment>> fragmentMap;

private ChildFragmentAnimationManager() {
    fragmentMap = new HashMap<Fragment, List<Fragment>>();
}

public static ChildFragmentAnimationManager instance() {
    if (instance == null) {
        instance = new ChildFragmentAnimationManager();
    }
    return instance;
}

public FragmentTransaction animate(FragmentTransaction ft, Fragment parent) {
    List<Fragment> children = getChildren(parent);

    ft.setCustomAnimations(R.anim.no_anim, R.anim.no_anim, R.anim.no_anim, R.anim.no_anim);
    for (Fragment child : children) {
        ft.remove(child);
    }

    return ft;
}

public void putChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.add(child);
}

public void removeChild(Fragment parent, Fragment child) {
    List<Fragment> children = getChildren(parent);
    children.remove(child);
}

private List<Fragment> getChildren(Fragment parent) {
    List<Fragment> children;

    if ( fragmentMap.containsKey(parent) ) {
        children = fragmentMap.get(parent);
    } else {
        children = new ArrayList<Fragment>(3);
        fragmentMap.put(parent, children);
    }

    return children;
}

}

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.

public abstract class BaseFragment extends Fragment {

@Override
public  View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().putChild(parent, this);
    }

    return super.onCreateView(inflater, container, savedInstanceState);
}

@Override
public void onDestroyView() {
    Fragment parent = getParentFragment();
    if (parent != null) {
        ChildFragmentAnimationManager.instance().removeChild(parent, this);
    }

    super.onDestroyView();
}

}

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.

FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ChildFragmentAnimationManager.instance().animate(ft, ReaderFragment.this)
                    .setCustomAnimations(R.anim.up_in, R.anim.up_out, R.anim.down_in, R.anim.down_out)
                    .replace(R.id.container, f)
                    .addToBackStack(null)
                    .commit();

Além disso, para que você tenha, aqui está o arquivo no_anim.xml que vai para sua pasta res / anim:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator">
    <translate android:fromXDelta="0" android:toXDelta="0"
        android:duration="1000" />
</set>

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.

spierce7
fonte
1

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 FragmentAe FragmentB, ambos com fragmentos. Agora, quando você normalmente faria:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .remove(fragmentA)    <-------------------------------------------
  .addToBackStack(null)
  .commit()

Ao invés você faz

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .add(R.id.fragmentHolder, new FragmentB())
  .hide(fragmentA)    <---------------------------------------------
  .addToBackStack(null)
  .commit()

fragmentA.removeMe = true;

Agora, para a implementação do Fragmento:

public class BaseFragment extends Fragment {

    protected Boolean detachMe = false;
    protected Boolean removeMe = false;

    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        if (nextAnim == 0) {
            if (!enter) {
                onExit();
            }

            return null;
        }

        Animation animation = AnimationUtils.loadAnimation(getActivity(), nextAnim);
        assert animation != null;

        if (!enter) {
            animation.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    onExit();
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                }
            });
        }

        return animation;
    }

    private void onExit() {
        if (!detachMe && !removeMe) {
            return;
        }

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        if (detachMe) {
            fragmentTransaction.detach(this);
            detachMe = false;
        } else if (removeMe) {
            fragmentTransaction.remove(this);
            removeMe = false;
        }
        fragmentTransaction.commit();
    }
}
Danilo
fonte
O popBackStack não causaria um erro porque está tentando mostrar um fragmento que foi desanexado?
Alexandre
1

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

<?xml version="1.0" encoding="utf-8"?>    
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:propertyName="alpha"
        android:valueFrom="1.0"
        android:valueTo="1.0"
        android:duration="@integer/keep_child_fragment_animation_duration" />
</set>

A animação é então aplicada quando o fragmento do mapa é adicionado ao fragmento pai.

Fragmento pai

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.map_parent_fragment, container, false);

    MapFragment mapFragment =  MapFragment.newInstance();

    getChildFragmentManager().beginTransaction()
            .setCustomAnimations(R.animator.keep_child_fragment, 0, 0, 0)
            .add(R.id.map, mapFragment)
            .commit();

    return view;
}

Finalmente, a duração da animação do fragmento filho é definida em um arquivo de recurso.

valores / integers.xml

<resources>
  <integer name="keep_child_fragment_animation_duration">500</integer>
</resources>
Evgenii
fonte
0

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.

View.OnClickListener() {//this is from custom button but you can listen for back button pressed
            @Override
            public void onClick(View v) {
                getChildFragmentManager().popBackStack();
                //and here we can manage other fragment operations 
            }
        });

  Fragment fr = MyNeastedFragment.newInstance(product);

  getChildFragmentManager()
          .beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
                .replace(R.neasted_fragment_container, fr)
                .addToBackStack("Neasted Fragment")
                .commit();
Mateusz Biedron
fonte
0

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:

   override fun onCreateAnimator(transit: Int, enter: Boolean, nextAnim: Int): Animator {
    if (isConfigChange) {
        resetStates()
        return nothingAnim()
    }

    if (parentFragment is ParentFragment) {
        if ((parentFragment as BaseFragment).isPopping) {
            return nothingAnim()
        }
    }

    if (parentFragment != null && parentFragment.isRemoving) {
        return nothingAnim()
    }

    if (enter) {
        if (isPopping) {
            resetStates()
            return pushAnim()
        }
        if (isSuppressing) {
            resetStates()
            return nothingAnim()
        }
        return enterAnim()
    }

    if (isPopping) {
        resetStates()
        return popAnim()
    }

    if (isSuppressing) {
        resetStates()
        return nothingAnim()
    }

    return exitAnim()
}

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

Zafrani
fonte
0

Uma maneira simples de corrigir esse problema é usar a Fragmentclasse 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 ContractFragmentque você pode achar útil para construir seus aplicativos aproveitando a relação de fragmento pai-filho.

Mark Salpeter
fonte
0

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.

public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {

    final Fragment parent = getParentFragment();

    Fragment parentOfParent = null;

    if( parent!=null ) {
        parentOfParent = parent.getParentFragment();
    }

    if( !enter && parent != null && parentOfParent!=null && parentOfParent.isRemoving()){
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else
    if (!enter && parent != null && parent.isRemoving()) {
        // This is a workaround for the bug where child fragments disappear when
        // the parent is removed (as all children are first removed from the parent)
        // See https://code.google.com/p/android/issues/detail?id=55228
        Animation doNothingAnim = new AlphaAnimation(1, 1);
        doNothingAnim.setDuration(getNextAnimationDuration(parent, DEFAULT_CHILD_ANIMATION_DURATION));
        return doNothingAnim;
    } else {
        return super.onCreateAnimation(transit, enter, nextAnim);
    }
}
Alekshandru
fonte
0

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.

            val ft = fragmentManager?.beginTransaction()
            ft?.setCustomAnimations(R.anim.enter_from_right,
                    R.anim.exit_to_right)
            if (parentFragment.isHidden()) {
                ft?.show(vehicleModule)
            } else {
                ft?.hide(vehicleModule)
            }
            ft?.commit()

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.

Ed Manners
fonte
0

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:

val currentFragment = supportFragmentManager.findFragmentByTag(TAG)
val transaction = supportFragmentManager
    .beginTransaction()
    .setCustomAnimations(anim1, anim2, anim1, anim2)
    .add(R.id.fragmentHolder, FragmentB(), TAG)
if (currentFragment != null) {
    transaction.hide(currentFragment).commit()
    Handler().postDelayed({
        supportFragmentManager.beginTransaction().remove(currentFragment).commit()
    }, DURATION_OF_ANIM)
} else {
    transaction.commit()
}

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.

Jeppeman
fonte