Pop le fragment backstack sans jouer la Pop-Animation
je pousse un fragment sur la pile de fragment en utilisant le code suivant:
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 cette façon, lorsque la pile de fragments est activée, par exemple en appuyant sur le bouton arrière, une animation pop fragment est jouée. Cependant, il y a des situations dans lesquelles je voudrais poper le fragment backstack sans montrer cette animation, par exemple parce que je viens de revenir d'une autre activité et que je veux afficher le fragment précédent immédiatement, sans animation.
Un exemple de navigation pourrait ressembler à ceci:
- L'utilisateur sur l'écran de démarrage avec le fragment de racine
- il sélectionne un élément sur le fragment racine qui affiche alors un nouveau fragment pour montrer les détails de cet élément. Il le fait en utilisant une transaction de fragment qui définit les animations à la fois pour le cas push et pop (donc quand l'utilisateur appuie sur le bouton back, la transition est animée)
- à partir de ce fragment il commence une activité qui (pour quelque raison que ce soit) supprime l'élément qui était juste montré
- quand cette activité se termine, je voudrais revenir au fragment racine sans montrer l '"animation pop" du "fragment détail"
y a-t-il un moyen de faire apparaître le fragment backstack sans jouer l'animation pop spécifiée?
10 réponses
donc Warpzit était sur la bonne voie, il n'a tout simplement pas abordé votre problème spécifique trop bien. Je suis tombé sur exactement le même problème et voici comment je l'ai résolu.
J'ai d'abord créé une variable booléenne statique (pour simplifier, mettons-la dans la classe FragmentUtils)...
public class FragmentUtils {
public static boolean sDisableFragmentAnimations = false;
}
ensuite, dans chaque fragment que vous avez, vous devez outrepasser la méthode uncreateanimation...
@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);
}
puis, quand vous avez besoin de nettoyer les coulisses de votre activité simplement procédez de la manière suivante...
public void clearBackStack() {
FragmentUtils.sDisableFragmentAnimations = true;
getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
FragmentUtils.sDisableFragmentAnimations = false;
}
et voilà, un appel à clearBackStack () vous ramènera dans le fragment racine sans aucune animation de transition.
espérons que le grand G ajoutera une façon moins stupide de le faire à l'avenir.
donc pour la bibliothèque de support suivant les travaux:
dans le fragment qui doit avoir une animation pop personnalisée, vous outrepassez l'onCreateAnimation avec votre propre animation personnalisée. Vous pouvez l'obtenir et définir une sorte de paramètre en fonction de ce que vous voulez. Il peut être nécessaire de faire un travail supplémentaire pour le faire fonctionner avec régulièrement des fragments.
Voici l'exemple où je l'écrase et change la durée:
@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 {
}
L'utilisateur sur l'écran de démarrage avec le fragment de racine
disons que le fragment de racine est contenu dans L'activité A.
il sélectionne un élément sur le fragment racine qui affiche alors un nouveau fragment pour montrer les détails de cet élément. Il le fait en utilisant une transaction de fragment qui définit les animations à la fois pour le cas push et pop (donc quand l'utilisateur appuie sur le bouton back, la transition est animée)
la transaction est ajouté à la pile de retour. Ce qui signifie que lorsque le bouton de retour est pressé à partir du fragment de détail, le processus de sautage est animé.
A partir de ce fragment, il commence une activité qui (pour quelque raison que ce soit) supprime l'élément qui vient d'être montré.
disons que C'est L'activité B
lorsque cette activité se termine, je voudrais revenir au fragment racine sans montrer l '"animation pop" du " détail fragment"
une façon d'obtenir ce comportement est de le faire dans L'activité 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();
cela va démarrer L'activité en la réinitialisant à son état de racine selon le documentation.(cochez le dernier paragraphe de la section qui dit "Ce mode de lancement peut aussi être utilisé avec un bon effet en conjonction avec FLAG_ACTIVITY_NEW_TASK:......")
Avec cette configuration, l'animation sera présente dans le cas par défaut tandis que cas particulier vous pouvez contrôler l'animation en utilisant :
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
Qui commence une nouvelle activité sans animations. Si vous voulez une animation, vous pouvez le faire en utilisant le overridePendingTransition
méthode.
alors, j'aimerais suggérer un petit changement à la réponse de @Geoff.
au lieu d'avoir un booléen statique global, je préférerais avoir un booléen non statique local. C'est ce que je suis venu avec.
Créer une interface
public interface TransitionAnimator {
void disableTransitionAnimation();
void enableTransitionAnimation();
}
faire en sorte que le fragment implémente cette interface.
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;
}
et puis, quand vous voulez désactiver les animations de transition pour un fragment, faites juste
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).disableTransitionAnimation();
}
pour les activer, il suffit de faire
if (fragment instanceof TransitionAnimator) {
((TransitionAnimator) fragment).enableTransitionAnimation();
}
si vous voulez faire la même chose pour tous les fragments dans le gestionnaire de fragments, faites juste
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof TransitionAnimator) {
// disable animations
((TransitionAnimator) fragment).disableTransitionAnimation();
}
}
très similaire, mais sans champs statiques.
il suffit D'utiliser une autre méthode desetCustomAnimation()
et dans lesquels ne sont pas positionnés les R. anim.slide_out
et qui permettra de résoudre votre problème
Cheers :)
avant de répondre à votre question, je dois poser une question moi-même.
dans la méthode onBackPressed () de la deuxième activité, pouvez-vous accéder aux coulisses de la première activité?
si oui, alors vous pouvez appeler popBackStackImmediate (String trnaisiotnName, int inclusive) et il supprimera la transition de fragment de la backstack, et vous n'aurez pas à vous soucier des animations.
je suppose que vous pouvez accéder aux coulisses de l'activité précédente, sinon cela ne marchera pas
C'est assez facile à réaliser par overridePendingTransition(int enterAnim, int exitAnim)
avec les deux
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
overridePendingTransition(0, 0);
}
il s'agit d'un suivi de l'excellente réponse de @Geoff, mais adapté à un scénario plus dynamique et réel.
j'ai imaginé que c'était un joli petit post, mais je me rends compte maintenant qu'il est devenu un peu hors de contrôle. Cependant, le code est tout là et je le trouve vraiment utile, bien qu'il couvre beaucoup plus que juste comment désactiver les animations de transition.
en général, quand je travaille avec des Fragments, j'aime avoir un fragment de base qui se fixe à un BaseActivityCallback. Ce BaseActivityCallback peut être utilisé par les Fragments my pour ajouter un nouveau Fragment sur lui-même, ou même des Fragments pop en dessous, d'où le désir de désactiver les animations pop -- ou pop silencieusement:
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://stackoverflow.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://stackoverflow.com/a/11253987/2412477
}
}
Maintenant vous pouvez créer votre MainActivity
et ont de ce spectacle un Fragment1
ce qui peut ajouter un autre Fragment2
qui peut à son tour pop Fragment1
silencieusement:
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 );
}
});
}
}
outrepasser cela dans le fragment que vous voulez pop sans animation et garder toujours l'animation quand vous entrez
@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);
}
réponse au Commentaire de Geoff et plackemacher.
Vous pouvez essayer de supprimer toutes les vues de ce Fragment. Puis fragment montrera mais il doit être transparent.
tout Supprimer-1 (j'utilise naviguer tiroir afin de tiroir fragment doit rester) fragment:
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);
}
Désolé pour mon anglais