android - popbackstack - Pop el fragmento de backstack sin jugar la Pop-Animation
fragment back navigation android (10)
Empujo un fragmento en la pila de fragmentos usando el siguiente código:
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_right,
R.anim.slide_in_left, R.anim.slide_out_left);
fragmentTransaction.replace(getId(), newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
De esta manera, cuando se hace estallar la pila de fragmentos, por ejemplo presionando el botón Atrás, se reproduce una animación de fragmento de pop. Sin embargo, hay situaciones en las que me gustaría mostrar el fragmento de backstack sin mostrar esta animación, por ejemplo, porque acabo de regresar de otra actividad y quiero mostrar el fragmento anterior a la vez, sin animación.
Un ejemplo de navegación podría verse así:
- El usuario está en la pantalla de inicio con el fragmento raíz
- Selecciona un elemento en el fragmento raíz que luego muestra un nuevo fragmento para mostrar los detalles de ese elemento. Lo hace mediante una transacción de fragmento que establece animaciones tanto para el push como para el pop (de modo que cuando el usuario presiona el botón Atrás, la transición se anima)
- A partir de este fragmento comienza una actividad que (por cualquier motivo) borra el elemento que se acaba de mostrar
- Cuando termine esta actividad, me gustaría volver al fragmento raíz sin mostrar la "animación pop" del "fragmento de detalle"
¿Hay alguna forma de mostrar el fragmento de backstack sin reproducir la animación pop especificada?
El usuario está en la pantalla de inicio con el fragmento raíz
Digamos que el fragmento raíz está contenido en la Actividad A.
Selecciona un elemento en el fragmento raíz que luego muestra un nuevo fragmento para mostrar los detalles de ese elemento. Lo hace mediante una transacción de fragmento que establece animaciones tanto para el push como para el pop (de modo que cuando el usuario presiona el botón Atrás, la transición se anima)
La transacción se agrega a la pila posterior. Lo que significa que cuando se presiona el botón Atrás desde el fragmento de detalle, el proceso de estallido se anima.
A partir de este fragmento, inicia una actividad que (por el motivo que sea) elimina el elemento que se acaba de mostrar.
Digamos que es Actividad B
Cuando termine esta actividad, me gustaría volver al fragmento raíz sin mostrar la "animación pop" del "fragmento de detalle"
Una forma de obtener este comportamiento es haciendo esto en la Actividad B:
Intent intent = new Intent(this, A.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
Esto iniciará la Actividad A reiniciándolo a su estado raíz de acuerdo con la documentation . (Verifique el último párrafo en la sección que dice "Este modo de lanzamiento también se puede usar con buenos resultados junto con FLAG_ACTIVITY_NEW_TASK: ......" )
Con esta configuración, la animación estará presente en el caso predeterminado, mientras que en el caso especial podrá controlar la animación usando:
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Que inicia una nueva actividad sin animaciones. Si desea alguna animación, puede hacerlo utilizando el método de overridePendingTransition
.
Antes de responder a su pregunta, necesito hacer una pregunta yo mismo.
En el método onBackPressed () de la segunda actividad, ¿puedes acceder al backstack de la primera actividad?
Si es así, puede llamar a popBackStackImmediate (String trnaisiotnName, int inclusive) y eliminará la transición de fragmentos de la backstack, y no tendrá que preocuparse por las animaciones.
Supongo que puedes acceder al backstack de la actividad anterior, de lo contrario, esto no funcionará
Anule esto en el fragmento que desea mostrar sin animación y mantenga la animación cuando ingrese
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if(!enter){
Animation a = new Animation() {};
a.setDuration(0);
return a;
}
return super.onCreateAnimation(transit, enter, nextAnim);
}
Así que Warpzit iba por buen camino, simplemente no resolvió su problema específico demasiado bien. Me encontré con el mismo problema y así es como lo resolví.
Primero creé una variable booleana estática (por simplicidad, permítanme ponerla en la clase FragmentUtils) ...
public class FragmentUtils {
public static boolean sDisableFragmentAnimations = false;
}
Luego, en CADA fragmento que tiene, debe anular el método onCreateAnimation ...
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if (FragmentUtils.sDisableFragmentAnimations) {
Animation a = new Animation() {};
a.setDuration(0);
return a;
}
return super.onCreateAnimation(transit, enter, nextAnim);
}
Luego, cuando necesite despejar el backstack de su actividad simplemente haga lo siguiente ...
public void clearBackStack() {
FragmentUtils.sDisableFragmentAnimations = true;
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentUtils.sDisableFragmentAnimations = false;
}
Y listo, una llamada a clearBackStack () te devolverá al fragmento raíz sin ninguna animación de transición.
Con suerte, la gran G agregará una forma menos estúpida de hacer esto en el futuro.
Entonces, me gustaría sugerir un pequeño cambio a la respuesta de @ Geoff.
En lugar de tener un booleano estático global, prefiero tener uno local no estático. Esto es lo que se me ocurrió.
Crea una interfaz
public interface TransitionAnimator {
void disableTransitionAnimation();
void enableTransitionAnimation();
}
Haz que el fragmento implemente esa interfaz.
public class MyFragment extends Fragment implements TransitionAnimator {
private boolean mTransitionAnimation;
@Override
public void disableTransitionAnimation() {
mTransitionAnimation = false;
}
@Override
public void enableTransitionAnimation() {
mTransitionAnimation = true;
}
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
Animation result;
if (!mTransitionAnimation) {
Animation dummyAnimation = new Animation() {
};
dummyAnimation.setDuration(0);
result = dummyAnimation;
} else {
result = super.onCreateAnimation(transit, enter, nextAnim);
}
return result;
}
Y luego, cuando desee deshabilitar las animaciones de transición para un fragmento, solo haga
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).disableTransitionAnimation();
}
para habilitarlos, solo hazlo
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).enableTransitionAnimation();
}
Si quieres hacer lo mismo para todos los fragmentos en el administrador de fragmentos, solo hazlo
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof TransitionAnimator) {
// disable animations
((TransitionAnimator) fragment).disableTransitionAnimation();
}
}
Muy similar, pero sin campos estáticos.
Entonces, para la biblioteca de soporte que sigue los trabajos:
En el fragmento que debería tener una animación pop personalizada, anulas el onCreateAnimation con el tuyo personalizado. Puedes obtenerlo y establecer algún tipo de parámetro dependiendo de lo que quieras. Es posible que haya que hacer un trabajo adicional para que funcione con fragmentos regulares.
Aquí está el ejemplo donde lo anulo y cambio la duración establecida:
@Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
Animation anim = (Animation) super.onCreateAnimation(transit, enter, nextAnim);
if(!enter) {
if(anim != null) {
anim.setDuration(0); // This doesn''t seem to be called.
return anim;
} else {
Animation test = new TestAnimation();
test.setDuration(0);
return test;
}
}
return anim;
}
private class TestAnimation extends Animation {
}
Este es un seguimiento de la excelente respuesta de @ Geoff, pero adecuado para un escenario más dinámico y real.
Me imaginaba que se trataba de una pequeña y agradable publicación, pero ahora me doy cuenta de que se salió un poco de las manos. Sin embargo, el código está ahí y lo encuentro realmente útil, aunque cubre mucho más que solo cómo desactivar las animaciones de transición.
Usualmente, cuando trabajo con Fragments, me gusta tener un BaseFragment que se una a BaseActivityCallback. Esta BaseActivityCallback puede ser utilizada por mis Fragmentos para agregar un nuevo Fragmento encima de sí mismo, o incluso para hacer estallar Fragmentos debajo de él, de ahí el deseo de deshabilitar las animaciones pop - o pop silenciosamente :
interface BaseActivityCallback
{
void addFragment ( BaseFragment f, int containerResId );
void popFragment ( boolean silently );
}
class BaseActivity extends android.support.v4.app.FragmentActivity implements BaseActivityCallback
{
public void addFragment ( BaseFragment f, int containerResId )
{
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.enter, R.anim.pop_exit); // http://.com/a/17488542/2412477
ft.addToBackStack(DEFAULT_FRAGMENT_STACK_NAME);
ft.replace(containerResId, fragment);
ft.commitAllowingStateLoss();
}
public void popFragment ( boolean silently )
{
FragmentManager fm = getSupportFragmentManager();
if ( silently ) {
int count = fm.getFragments().size();
BaseFragment f = (BaseFragment)fm.getFragments().get(count-1);
f.setDisableTransitionAnimations(true);
}
fm.popBackStackImmediate();
}
}
public abstract class BaseFragment extends android.support.v4.app.Fragment
{
private static final String TAG = "BaseFragment";
private final String STATE_DISABLE_TRANSITION_ANIMATIONS = TAG+".stateDisableTransitionAnimations";
protected BaseActivityCallback baseActivityCallback;
private boolean disableTransitionAnimations;
@Override
public void onCreate ( @Nullable Bundle savedInstanceState )
{
super.onCreate(savedInstanceState);
disableTransitionAnimations = (savedInstanceState==null ? false : savedInstanceState.getBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, false));
}
@Override
public void onAttach ( Context context )
{
super.onAttach(context);
baseActivityCallback = (BaseActivityCallback)context;
}
@Override
public void onSaveInstanceState ( Bundle outState )
{
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_DISABLE_TRANSITION_ANIMATIONS, disableTransitionAnimations);
}
@Override
public Animation onCreateAnimation ( int transit, boolean enter, int nextAnim )
{
if ( disableTransitionAnimations ) {
Animation nop = new Animation(){};
nop.setDuration(0);
return nop;
}
return super.onCreateAnimation(transit, enter, nextAnim);
}
public void setDisableTransitionAnimations ( boolean disableTransitionAnimations )
{
this.disableTransitionAnimations = disableTransitionAnimations; // http://.com/a/11253987/2412477
}
}
Ahora puede crear su MainActivity
y hacer que muestre un Fragment1
que puede agregar otro Fragment2
que a su vez puede hacer estallar Fragment1
silencio :
public class MainActivity extends BaseActivity
{
protected void onCreate ( Bundle savedInstanceState )
{
setContentView(R.layout.main_activity);
...
if ( getSupportFragmentManager().getFragments() != null && !getSupportFragmentManager().getFragments().isEmpty() ) {
addFragment( FragmentA.newInstance(), R.id.main_activity_fragment_container );
}
}
...
}
public class FragmentA extends BaseFragment
{
public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
{
ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_a, container, false);
...
root.findViewById(R.id.fragment_a_next_button)
.setOnClickListener( new View.OnClickListener() {
public void onClick ( View v ) {
baseActivityCallback.addFragment( FragmentB.newInstance(), R.id.main_activity_fragment_container );
}
});
}
}
public class FragmentB extends BaseFragment
{
public View onCreateView ( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
{
ViewGroup root = (ViewGroup)inflater.inflate(R.layout.fragment_b, container, false);
...
root.findViewById(R.id.fragment_b_pop_silently_button)
.setOnClickListener( new View.OnClickListener() {
public void onClick ( View v ) {
baseActivityCallback.popFragment( true );
}
});
}
}
Esto es bastante fácil de lograr overridePendingTransition(int enterAnim, int exitAnim)
con ambos 0
para ninguna animación.
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
overridePendingTransition(0, 0);
}
Responde a Geoff y comenta el placketmacher.
Puede intentar eliminar todas las vistas de este Fragmento. Entonces se mostrará el fragmento, pero debería ser transparente.
Eliminar todo-1 (utilizo el cajón de navegación para que el fragmento del cajón permanezca) fragmento:
int size = fragmentsList.size ()-1;
FragmentTransaction transaction = fragmentManager.beginTransaction ();
transaction.setTransition (FragmentTransaction.TRANSIT_NONE);
Fragment fragment;
for (int i = size ; i > 0 ; i--)
{
fragment = fragmentsList.get (i);
if(fragment != null)
{
View viewContainer = fragment.getView ();
if (viewContainer != null)
{
((ViewGroup) viewContainer).removeAllViews ();
}
transaction.remove (fragment);
}
}
size = fragmentManager.getBackStackEntryCount ();
for (int i = 0; i < size ; i++)
{
fragmentManager.popBackStack (null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
Lo siento por mi ingles
Solo use otro método sobrecargado de setCustomAnimation()
y en el cual no establezca el R.anim.slide_out y eso resolverá su problema
Saludos :)