Los fragmentos anidados desaparecen durante la animación de transición

100

Aquí está el escenario: La actividad contiene un fragmento A, que a su vez se usa getChildFragmentManager()para agregar fragmentos A1y A2de onCreateesa manera:

getChildFragmentManager()
  .beginTransaction()
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

Hasta ahora todo va bien, todo está funcionando como se esperaba.

Luego ejecutamos la siguiente transacción en la Actividad:

getSupportFragmentManager()
  .beginTransaction()
  .setCustomAnimations(anim1, anim2, anim1, anim2)
  .replace(R.id.fragmentHolder, new FragmentB())
  .addToBackStack(null)
  .commit()

Durante la transición, las enteranimaciones del fragmento se Bejecutan correctamente, pero los fragmentos A1 y A2 desaparecen por completo . Cuando revertimos la transacción con el botón Atrás, se inicializan correctamente y se muestran normalmente durante la popEnteranimación.

En mi breve prueba, se volvió más extraño: si configuro las animaciones para los fragmentos secundarios (ver más abajo), la exitanimación se ejecuta de forma intermitente cuando agregamos un fragmentoB

getChildFragmentManager()
  .beginTransaction()
  .setCustomAnimations(enter, exit)
  .replace(R.id.fragmentOneHolder, new FragmentA1())
  .replace(R.id.fragmentTwoHolder, new FragmentA2())
  .commit()

El efecto que quiero lograr es simple: quiero que se ejecute la animación exit(¿o debería ser popExit?) En el fragmento A(anim2), animando todo el contenedor, incluidos sus elementos secundarios anidados.

¿Hay alguna forma de lograrlo?

Editar : encuentre un caso de prueba aquí

Edit2 : Gracias a @StevenByle por empujarme a seguir intentando con las animaciones estáticas. Aparentemente, puede configurar animaciones por operación (no globales para toda la transacción), lo que significa que los niños pueden tener un conjunto de animación estática indefinida, mientras que su padre puede tener una animación diferente y todo se puede comprometer en una transacción . Vea la discusión a continuación y el proyecto de caso de prueba actualizado .

Delyan
fuente
¿Qué es R.id.fragmentHoldercon respecto a A, A1, A2, etc.?
CommonsWare
fragmentHolder es un id en el diseño de la actividad, el fragment {One, Two} Holder está en el diseño del fragment A. Los tres son distintos. El fragmento A se añadió inicialmente en fragmentHolder (es decir, el fragmento B reemplaza al fragmento A).
Delyan
He creado un proyecto de muestra aquí: github.com/BurntBrunch/NestedFragmentsAnimationsTest , también hay un apk incluido en el repositorio. Este es un error realmente molesto y estoy buscando una manera de solucionarlo (asumiendo que no está en mi código).
Delyan
Ahora sé un poco más sobre este tema. La razón por la que desaparecen los fragmentos es porque los hijos manejan los eventos del ciclo de vida antes que el padre. En esencia, A1 y A2 se eliminan antes que A y, como no tienen animaciones establecidas, desaparecen abruptamente. Una forma de mitigar esto de alguna manera es eliminar explícitamente A1 y A2 en la transacción que reemplaza A. De esa manera, se animan cuando salen, sin embargo, su velocidad de animación es al cuadrado, ya que el contenedor principal también se anima. Se agradecería una solución que no produzca este artefacto.
Delyan
El cambio (reemplazando el fragmento de inicio) que menciona en la pregunta es el real que desea hacer o es solo un ejemplo. ¿Llamarás al changeFragmentmétodo solo una vez?
Luksprog

Respuestas:

36

Para evitar que el usuario vea que los fragmentos anidados desaparecen cuando el fragmento principal se elimina / reemplaza en una transacción, puede "simular" esos fragmentos que aún están presentes proporcionando una imagen de ellos, tal como aparecieron en la pantalla. Esta imagen se utilizará como fondo para el contenedor de fragmentos anidados, por lo que incluso si las vistas del fragmento anidado desaparecen, la imagen simulará su presencia. Además, no veo que perder la interactividad con las vistas del fragmento anidado sea un problema porque no creo que desee que el usuario actúe sobre ellos cuando están en el proceso de ser eliminado (probablemente como una acción del usuario como bien).

Hice un pequeño ejemplo con la configuración de la imagen de fondo (algo básico).

Luksprog
fuente
1
Decidí otorgarte la recompensa, ya que esa fue la solución que terminé usando. ¡Muchísimas gracias por su tiempo!
Delyan
16
Vaya, muy sucio. Las longitudes nosotros Desarrolladores de Android tienen que ir a sólo para algunos slickness
Dean Wild
1
Tengo un visor de la segunda pestaña, estoy reemplazando otro fragmento y cuando presiono hacia atrás necesito mostrar la segunda pestaña del visor, se abre pero muestra una página en blanco. Intenté lo que sugirió en el hilo anterior, pero sigue siendo el mismo.
Harish
El problema persiste cuando el usuario regresa como escribió @Harish
Ewoks
1
Wow en serio? es 2018 y esto sigue siendo una cosa? :(
Archie G. Quiñones
69

Así que parece haber muchas soluciones alternativas para esto, pero según la respuesta de @ Jayd16, creo que encontré una solución general bastante sólida que aún permite animaciones de transición personalizadas en fragmentos secundarios y no requiere hacer un caché de mapa de bits del diseño.

Tenga una BaseFragmentclase que se extienda Fragmenty haga que todos sus fragmentos extiendan esa clase (no solo los fragmentos secundarios).

En esa BaseFragmentclase, agregue lo siguiente:

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

Desafortunadamente, requiere reflexión; sin embargo, dado que esta solución es para la biblioteca de soporte, no corre el riesgo de que cambie la implementación subyacente a menos que actualice su biblioteca de soporte. Si está creando la biblioteca de soporte desde la fuente, puede agregar un acceso para el siguiente ID de recurso de animación Fragment.javay eliminar la necesidad de reflexión.

Esta solución elimina la necesidad de "adivinar" la duración de la animación del padre (de modo que la animación de "no hacer nada" tenga la misma duración que la animación de salida del padre) y le permite seguir haciendo animaciones personalizadas en los fragmentos secundarios (por ejemplo, si ' intercambiando fragmentos de niños con diferentes animaciones).

Kevin Coppock
fuente
5
Esta es mi solución favorita en el hilo. No requiere un mapa de bits, no requiere ningún código adicional en el fragmento secundario y realmente no filtra información del fragmento principal al secundario.
jacobhyphenated
1
@EugenPechanec Necesitas el nextAnim del fragmento principal , no el del hijo. Ese es todo el punto.
Kevin Coppock
1
Desafortunadamente, este enfoque causa una pérdida de memoria para los fragmentos secundarios e implícitamente también para el fragmento principal en las versiones de Android por debajo de Lollipop :(
Cosmin
8
Gracias por esta solución tan útil, sin embargo, requiere una pequeña actualización para funcionar con la biblioteca de soporte actual (27.0.2, no sé qué versión rompió este código). mNextAnimahora está dentro de un mAnimationInfoobjeto. Puede acceder a él así:Field animInfoField = Fragment.class.getDeclaredField("mAnimationInfo"); animInfoField.setAccessible(true); Object animationInfo = animInfoField.get(fragment); Field nextAnimField = animationInfo.getClass().getDeclaredField("mNextAnim");
David Lericolais
5
@DavidLericolais, me gustaría agregar otra línea de código después de la suya. val nextAnimResource = nextAnimField.getInt(animationInfo);para reemplazar la líneaint nextAnimResource = nextAnimField.getInt(fragment);
tingyik90
32

Pude encontrar una solución bastante limpia. En mi opinión, es el menos hacky, y si bien esta es técnicamente la solución de "dibujar un mapa de bits", al menos está abstraída por el fragmento lib.

Asegúrese de que los fragmentos de su hijo anulen una clase principal con esto:

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

Si tenemos una animación de salida en los fragmentos secundarios, se animarán en lugar de parpadear. Podemos aprovechar esto al tener una animación que simplemente dibuja los fragmentos secundarios en alfa completo durante un tiempo. De esta manera, permanecerán visibles en el fragmento principal mientras se anima, dando el comportamiento deseado.

El único problema que se me ocurre es hacer un seguimiento de esa duración. Tal vez podría configurarlo en un número grande, pero me temo que eso podría tener problemas de rendimiento si todavía está dibujando esa animación en alguna parte.

Jayd16
fuente
Ayuda, gracias. El valor del tiempo de duración no importa
iscariot
solución más limpia hasta ahora
Liran Cohen
16

Estoy publicando mi solución para mayor claridad. La solución es bastante simple. Si está intentando imitar la animación de la transacción del fragmento principal, simplemente agregue una animación personalizada a la transacción del fragmento secundario con la misma duración. Ah, y asegúrese de configurar la animación personalizada antes de agregar ().

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

El xml para R.anim.none (el tiempo de animación de entrada / salida de mis padres es de 250 ms)

<?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
fuente
Hice algo muy similar, pero usé "mostrar" en lugar de "agregar" al actualizar el niño. También agregué "getChildFragmentManager (). ExecutePendingTransactions ()", aunque no estoy seguro si eso es estrictamente necesario. Sin embargo, esta solución funciona bien y no requiere "proporcionar una imagen" del Fragmento como algunos sugieren.
Brian Yencho
Esto fue genial. Sin embargo, estaba recibiendo el retraso al cambiar los fragmentos de mi hijo. Para evitar esto, acaba de establecer el segundo parámetro sin anim:fragmentTransaction.setCustomAnimations(R.anim.none, 0, R.anim.none, R.anim.none)
ono
7

Entiendo que es posible que esto no pueda resolver completamente su problema, pero tal vez se adapte a las necesidades de otra persona, puede agregar enter/ exity popEnter/ popExitanimaciones a los de sus hijos Fragmentque en realidad no mueven / animan los Fragments. Siempre que las animaciones tengan la misma duración / desplazamiento que sus Fragmentanimaciones principales , parecerán que se mueven / animan con la animación principal.

Steven Byle
fuente
1
Le he otorgado la recompensa a Luksprog ya que su solución funciona universalmente. Probé el truco de animaciones estáticas (la duración en realidad no importa; una vez que el padre se ha ido, las vistas obviamente desaparecen) pero no funcionaron en todos los casos posibles (vea mis comentarios debajo de la pregunta). Además, este enfoque filtra la abstracción, ya que el padre de un fragmento con niños necesita conocer ese hecho y tomar medidas adicionales para configurar las animaciones de los niños. En cualquier caso, ¡muchas gracias por tu tiempo!
Delyan
De acuerdo, esto es más una solución que una solución hermética, y puede considerarse un poco frágil. Pero funcionará para casos simples.
Steven Byle
4

puedes hacer esto en el fragmento secundario.

@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
fuente
¡Gracias! Quizás no sea la mejor, pero posiblemente la solución más simple.
bug56
2

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

EDITAR: Terminé sin implementar esta solución ya que hubo otros problemas que esto tiene. Square lanzó recientemente 2 bibliotecas que reemplazan los fragmentos. Yo diría que esta podría ser una alternativa mejor que intentar piratear fragmentos para que hagan algo que Google no quiere que hagan.

http://corner.squareup.com/2014/01/mortar-and-flow.html

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

Pensé que pondría esta solución para ayudar a las personas que tengan este problema en el futuro. Si sigue la conversación de los carteles originales con otras personas y observa el código que publicó, verá que el cartel original finalmente llega a la conclusión de usar una animación no operativa en los fragmentos secundarios mientras anima el fragmento principal. Esta solución no es ideal, ya que le obliga a realizar un seguimiento de todos los fragmentos secundarios, lo que puede resultar engorroso cuando se utiliza un ViewPager con FragmentPagerAdapter.

Como utilizo Child Fragments en todas partes, se me ocurrió esta solución que es eficiente y modular (por lo que se puede quitar fácilmente) en caso de que alguna vez lo solucionen y esta animación no operativa ya no sea necesaria.

Hay muchas formas de implementar esto. Elegí usar un singleton, y lo llamo ChildFragmentAnimationManager. Básicamente, realizará un seguimiento de un fragmento secundario para mí en función de su padre y aplicará una animación no operativa a los niños cuando se le solicite.

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

}

A continuación, debe tener una clase que extienda Fragmento que se extiendan todos sus Fragmentos (al menos sus Fragmentos secundarios). Ya tenía esta clase y la llamo BaseFragment. Cuando se crea una vista de fragmentos, la agregamos al ChildFragmentAnimationManager y la eliminamos cuando se destruye. Puede hacer esto en Adjuntar / Separar u otros métodos de coincidencia en la secuencia. Mi lógica para elegir Crear / Destruir Vista fue porque si un Fragmento no tiene una Vista, no me importa animarlo para que se siga viendo. Este enfoque también debería funcionar mejor con ViewPagers que usan Fragments, ya que no hará un seguimiento de cada FragmentPagerAdapter que contiene un FragmentPagerAdapter, sino solo 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();
}

}

Ahora que todos sus Fragmentos están almacenados en la memoria por el fragmento principal, puede llamar a animarlos de esta manera, y sus fragmentos secundarios no desaparecerán.

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

Además, para que lo tenga, aquí está el archivo no_anim.xml que va en su carpeta 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>

Nuevamente, no creo que esta solución sea perfecta, pero es mucho mejor que para cada instancia que tenga un fragmento secundario, implementando código personalizado en el fragmento principal para realizar un seguimiento de cada niño. He estado allí y no es divertido.

spierce7
fuente
1

Creo que encontré una mejor solución a este problema que hacer una instantánea del fragmento actual en un mapa de bits como sugirió Luksprog.

El truco consiste en ocultar el fragmento que se está eliminando o separando y solo después de que se hayan completado las animaciones, el fragmento se elimina o se separa en su propia transacción de fragmentos.

Imagina que tenemos FragmentAy FragmentB, ambos con subfragmentos. Ahora, cuando lo haría normalmente:

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

En cambio lo haces

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

fragmentA.removeMe = true;

Ahora para la implementación del 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
fuente
¿No provocaría el popBackStack un error porque está intentando mostrar un fragmento que se ha separado?
Alexandre
1

Estaba teniendo el mismo problema con el fragmento de mapa. Siguió desapareciendo durante la animación de salida de su fragmento contenedor. La solución es agregar animación para el fragmento de mapa secundario que lo mantendrá visible durante la animación de salida del fragmento principal. La animación del fragmento secundario mantiene su alfa al 100% durante su período de duración.

Animación: 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>

Luego, la animación se aplica cuando el fragmento de mapa se agrega al fragmento principal.

Fragmento principal

@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, la duración de la animación del fragmento secundario se establece en un archivo de recursos.

valores / integers.xml

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

Para animar la desaparición de fragmentos neasted, podemos forzar la pila de retroceso en ChildFragmentManager. Esto disparará la animación de transición. Para hacer esto, necesitamos ponernos al día con el evento OnBackButtonPressed o escuchar los cambios de backstack.

Aquí hay un ejemplo con 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
fuente
0

Recientemente me encontré con este problema en mi pregunta: fragmentos anidados que hacen una transición incorrecta

Tengo una solución que resuelve esto sin guardar un mapa de bits, ni usar la reflexión ni ningún otro método insatisfactorio.

Se puede ver un proyecto de ejemplo aquí: https://github.com/zafrani/NestedFragmentTransitions

Un GIF del efecto se puede ver aquí: https://imgur.com/94AvrW4

En mi ejemplo, hay 6 fragmentos secundarios, divididos entre dos fragmentos principales. Puedo lograr las transiciones para entrar, salir, hacer estallar y empujar sin ningún problema. Los cambios de configuración y las prensas posteriores también se gestionan con éxito.

La mayor parte de la solución está en la función onCreateAnimator de mi BaseFragment (el fragmento extendido por mis hijos y los fragmentos principales) que se ve así:

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

La actividad y el fragmento padre son responsables de establecer los estados de estos valores booleanos. Es más fácil ver cómo y dónde desde mi proyecto de ejemplo.

No estoy usando fragmentos de soporte en mi ejemplo, pero se puede usar la misma lógica con ellos y su función onCreateAnimation

zafrani
fuente
0

Una forma sencilla de solucionar este problema es utilizar la Fragmentclase de esta biblioteca en lugar de la clase de fragmento de biblioteca estándar:

https://github.com/marksalpeter/contract-fragment

Como nota al margen, el paquete también contiene un patrón de delegado útil llamado ContractFragmentque puede resultarle útil para crear sus aplicaciones aprovechando la relación de fragmento padre-hijo.

Mark Salpeter
fuente
0

De la respuesta anterior de @kcoppock,

si tiene Actividad-> Fragmento-> Fragmentos (apilamiento múltiple, lo siguiente ayuda), una edición menor a la mejor respuesta en mi humilde opinión.

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
fuente
0

Mi problema estaba en la eliminación del fragmento principal (ft.remove (fragment)), las animaciones secundarias no estaban sucediendo.

El problema básico es que los fragmentos secundarios se DESTRUYEN inmediatamente ANTES de que el fragmento principal salga de la animación.

Las animaciones personalizadas de los fragmentos secundarios no se ejecutan al eliminar el fragmento principal

Como otros han eludido, esconder al PADRE (y no al niño) antes de la remoción del PADRE es el camino a seguir.

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

Si realmente desea eliminar el padre, probablemente debería configurar un oyente en su animación personalizada para saber cuándo finaliza la animación, de modo que pueda finalizar con seguridad el fragmento principal (eliminar). Si no hace esto, de manera oportuna, podría terminar matando la animación. La animación NB se realiza en una cola asincrónica propia.

Por cierto, no necesita animaciones personalizadas en el fragmento secundario, ya que heredarán las animaciones principales.

Modales Ed
fuente
0

Hilo antiguo, pero en caso de que alguien se tropiece aquí:

Todos los enfoques anteriores me parecen muy poco atractivos, la solución de mapa de bits es muy sucia y no funciona; los otros requieren que los fragmentos secundarios conozcan la duración de la transición utilizada en la transacción utilizada para crear el fragmento secundario en cuestión. Una mejor solución en mis ojos es algo como lo siguiente:

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

Simplemente ocultamos el fragmento actual y agregamos el nuevo fragmento, cuando la animación ha terminado eliminamos el fragmento antiguo. De esta forma se maneja en un solo lugar y no se crea ningún mapa de bits.

jeppeman
fuente