web-dev-qa-db-fra.com

Exemple de travail complet du scénario d'animation Gmail à trois fragments?

TL; DR: Je recherche un exemple de travail complet de ce que je qualifierai de scénario "Animation Gmail à trois fragments". Spécifiquement, nous voulons commencer avec deux fragments, comme ceci:

two fragments

Lors de certains événements d'interface utilisateur (par exemple, en tapotant sur quelque chose dans le fragment B), nous voulons:

  • Fragment A pour glisser de l'écran vers la gauche
  • Fragment B pour glisser vers le bord gauche de l'écran et se rétrécir pour occuper la place laissée vacante par le fragment A
  • Fragment C à glisser du côté droit de l’écran et à occuper la place laissée vacante par le fragment B

Et, en appuyant sur le bouton BACK, nous voulons que cet ensemble d'opérations soit inversé.

Maintenant, j'ai vu beaucoup d'implémentations partielles; Je vais en examiner quatre ci-dessous. Au-delà d'être incomplets, ils ont tous leurs problèmes.


@Reto Meier a contribué cette réponse populaire à la même question de base, indiquant que vous utiliseriez setCustomAnimations() avec un FragmentTransaction. Pour un scénario à deux fragments (par exemple, vous ne voyez que le fragment A au début et souhaitez le remplacer par un nouveau fragment B utilisant des effets animés), je suis tout à fait d'accord. Toutefois:

  • Etant donné que vous ne pouvez spécifier qu'une animation "in" et une animation "out", je ne vois pas comment vous géreriez toutes les animations différentes requises pour le scénario à trois fragments.
  • Le code <objectAnimator> De son exemple utilise des positions câblées en pixels, et cela semblerait peu pratique compte tenu de la taille de l'écran, mais setCustomAnimations() nécessite des ressources d'animation, excluant ainsi la possibilité de définir ces éléments. en Java
  • Je ne sais pas comment les animateurs d'objets de la balance s'accordent avec des éléments tels que Android:layout_weight Dans un LinearLayout pour allouer de l'espace en pourcentage
  • Je ne sais pas comment le fragment C est traité au départ (GONE? Android:layout_weight Sur 0? Pré-animé à une échelle de 0? Autre chose?)

@ Roman Nurik fait remarquer que vous pouvez animer n’importe quelle propriété , y compris celles que vous définissez vous-même. Cela peut aider à résoudre le problème des positions câblées, au prix de l’invention de votre propre sous-classe de gestionnaire de disposition personnalisée. Cela aide certains, mais je suis encore dérouté par le reste de la solution de Reto.


L'auteur de cette entrée Pastebin montre un pseudo-code alléchant, indiquant que les trois fragments résideraient initialement dans le conteneur, avec le fragment C masqué au départ via une opération de transaction hide(). Nous avons ensuite show() C et hide() A lorsque l'événement d'interface utilisateur se produit. Cependant, je ne vois pas comment cela gère le fait que B change de taille. Il repose également sur le fait que vous pouvez apparemment ajouter plusieurs fragments au même conteneur, et je ne suis pas sûr que ce soit un comportement fiable à long terme (pour ne pas le dire, cela devrait casser findFragmentById(), bien que Je peux vivre avec ça).


L'auteur de this blog post indique que Gmail n'utilise pas du tout setCustomAnimations(), mais utilise directement des animateurs d'objet ("il suffit de changer la marge de gauche de la vue racine + de modifier la largeur de la vue de droite "). Toutefois, il s'agit toujours d'une solution AFAICT en deux fragments, et la mise en œuvre a montré une nouvelle fois les dimensions des câbles en pixels.


Je vais continuer à m'en tenir à cela, donc je vais peut-être finir par y répondre moi-même un jour, mais j'espère vraiment que quelqu'un aura mis au point la solution en trois fragments pour ce scénario d'animation et pourra poster le code (ou un lien vers celle-ci). Les animations dans Android me donnent envie de me tirer les cheveux, et ceux d’entre vous qui m’ont vu savent qu’il s’agit là d’une entreprise en grande partie stérile.

123
CommonsWare

OK, voici ma propre solution, dérivée de l'application de messagerie AOSP, conformément à la suggestion de @ Christopher dans les commentaires de la question.

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePane

La solution de @ faiblewire me rappelle la mienne, même s'il utilise des règles classiques Animation plutôt que des animateurs et utilise des règles RelativeLayout pour imposer le positionnement. Du point de vue de la prime, il obtiendra probablement la prime, à moins que quelqu'un d'autre avec une solution plus astucieuse publie encore une réponse.


En résumé, le ThreePaneLayout de ce projet est une sous-classe LinearLayout, conçue pour fonctionner en mode paysage avec trois enfants. Les largeurs de ces enfants peuvent être définies dans le XML de mise en page, via n'importe quel moyen souhaité - je le montre en utilisant des poids, mais vous pouvez avoir des largeurs spécifiques définies par des ressources de dimension ou autre. Le troisième enfant - Fragment C dans la question - devrait avoir une largeur de zéro.

package com.commonsware.Android.anim.threepane;

import Android.animation.Animator;
import Android.animation.AnimatorListenerAdapter;
import Android.animation.ObjectAnimator;
import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.LinearLayout;

public class ThreePaneLayout extends LinearLayout {
  private static final int ANIM_DURATION=500;
  private View left=null;
  private View middle=null;
  private View right=null;
  private int leftWidth=-1;
  private int middleWidthNormal=-1;

  public ThreePaneLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initSelf();
  }

  void initSelf() {
    setOrientation(HORIZONTAL);
  }

  @Override
  public void onFinishInflate() {
    super.onFinishInflate();

    left=getChildAt(0);
    middle=getChildAt(1);
    right=getChildAt(2);
  }

  public View getLeftView() {
    return(left);
  }

  public View getMiddleView() {
    return(middle);
  }

  public View getRightView() {
    return(right);
  }

  public void hideLeft() {
    if (leftWidth == -1) {
      leftWidth=left.getWidth();
      middleWidthNormal=middle.getWidth();
      resetWidget(left, leftWidth);
      resetWidget(middle, middleWidthNormal);
      resetWidget(right, middleWidthNormal);
      requestLayout();
    }

    translateWidgets(-1 * leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", middleWidthNormal,
                         leftWidth).setDuration(ANIM_DURATION).start();
  }

  public void showLeft() {
    translateWidgets(leftWidth, left, middle, right);

    ObjectAnimator.ofInt(this, "middleWidth", leftWidth,
                         middleWidthNormal).setDuration(ANIM_DURATION)
                  .start();
  }

  public void setMiddleWidth(int value) {
    middle.getLayoutParams().width=value;
    requestLayout();
  }

  private void translateWidgets(int deltaX, View... views) {
    for (final View v : views) {
      v.setLayerType(View.LAYER_TYPE_HARDWARE, null);

      v.animate().translationXBy(deltaX).setDuration(ANIM_DURATION)
       .setListener(new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
           v.setLayerType(View.LAYER_TYPE_NONE, null);
         }
       });
    }
  }

  private void resetWidget(View v, int width) {
    LinearLayout.LayoutParams p=
        (LinearLayout.LayoutParams)v.getLayoutParams();

    p.width=width;
    p.weight=0;
  }
}

Cependant, au moment de l'exécution, quelle que soit la configuration initiale des largeurs, la gestion de la largeur est prise en charge par ThreePaneLayout la première fois que vous utilisez hideLeft() pour ne plus afficher ce que la question appelle des fragments. A et B aux fragments B et C. Dans la terminologie de ThreePaneLayout - qui n’a aucun lien spécifique avec les fragments - les trois pièces sont left, middle et right. Au moment où vous appelez hideLeft(), nous enregistrons les tailles de left et middle et mettons à zéro tous les poids utilisés sur l'un des trois, afin de pouvoir contrôler complètement les tailles. Au moment de hideLeft(), nous avons défini la taille de right sur la taille d'origine de middle.

Les animations sont doubles:

  • Utilisez un ViewPropertyAnimator pour convertir les trois widgets situés à gauche de la largeur de left, à l'aide d'une couche matérielle.
  • Utilisez un ObjectAnimator sur une pseudo-propriété personnalisée de middleWidth pour modifier la largeur middle de ce qui a commencé par la largeur d'origine de left

(il est possible que ce soit une meilleure idée d'utiliser un AnimatorSet et un ObjectAnimators pour tous ces éléments, bien que cela fonctionne pour l'instant)

(il est également possible que middleWidthObjectAnimator annule la valeur de la couche matérielle, car une invalidation relativement continue est nécessaire)

(il est définitivement possible qu'il me reste des lacunes dans la compréhension de l'animation et que j'aime les énoncés entre parenthèses)

L’effet net est que left disparaît de l’écran, middle jusqu’à la position et à la taille originales de left et que right est traduit juste derrière middle.

showLeft() inverse simplement le processus, avec le même mélange d'animateurs, mais avec les directions inversées.

L'activité utilise un ThreePaneLayout pour contenir une paire de widgets ListFragment et un Button. Sélectionner quelque chose dans le fragment de gauche ajoute (ou met à jour le contenu) du fragment du milieu. Sélectionner quelque chose dans le fragment du milieu définit la légende du Button, plus exécute hideLeft() sur le ThreePaneLayout. En appuyant sur BACK, si nous avons caché le côté gauche, exécutera showLeft(); sinon, BACK quitte l'activité. Puisque ceci n'utilise pas FragmentTransactions pour affecter les animations, nous sommes bloqués à gérer cette "pile arrière" nous-mêmes.

Le projet lié ci-dessus utilise des fragments natifs et le framework d'animateur natif. J'ai une autre version du même projet qui utilise le backport de fragments de support Android) et NineOldAndroids pour l'animation:

https://github.com/commonsguy/cw-omnibus/tree/master/Animation/ThreePaneBC

Le backport fonctionne bien sur un Kindle Fire de 1ère génération, bien que l’animation soit un peu saccadée compte tenu des spécifications matérielles moins élevées et du manque de prise en charge de l’accélération matérielle. Les deux implémentations semblent lisses sur un Nexus 7 et d'autres tablettes de la génération actuelle.

Je suis certainement ouvert aux idées sur la façon d’améliorer cette solution ou à d’autres solutions qui offrent des avantages évidents par rapport à ce que j’ai fait ici (ou à ce que @weakwire a utilisé).

Merci encore à tous ceux qui ont contribué!

31
CommonsWare

Téléchargé ma proposition sur github (Travaille avec toutes les versions Android bien que l'accélération matérielle de visualisation soit fortement recommandée pour ce type d'animations. Pour les périphériques dont l'accélération n'est pas matérielle, une implémentation de la mise en cache bitmap devrait correspondre mieux)

La vidéo de démonstration avec l’animation est Ici (La cadence de prise de vue est un facteur lent de la diffusion de l’écran. Les performances réelles sont très rapides)


Utilisation:

layout = new ThreeLayout(this, 3);
layout.setAnimationDuration(1000);
setContentView(layout);
layout.getLeftView();   //<---inflate FragmentA here
layout.getMiddleView(); //<---inflate FragmentB here
layout.getRightView();  //<---inflate FragmentC here

//Left Animation set
layout.startLeftAnimation();

//Right Animation set
layout.startRightAnimation();

//You can even set interpolators

Explaination:

Création d'une nouvelle coutume RelativeLayout (ThreeLayout) et de 2 animations personnalisées (MyScalAnimation, MyTranslateAnimation).

ThreeLayout obtient le poids du volet gauche en tant que paramètre, en supposant que l'autre vue visible a weight=1.

Ainsi, new ThreeLayout(context,3) crée une nouvelle vue avec 3 enfants et le volet de gauche avec 1/3 de l’écran total. L'autre vue occupe tout l'espace disponible.

Il calcule la largeur au moment de l'exécution. Une implémentation plus sûre consiste à calculer les dimensions en premier dans draw (). au lieu de post ()

Les animations Scale and Translate redimensionnent et déplacent réellement la vue et non pas pseudo- [scale, move]. Notez que fillAfter(true) n'est utilisé nulle part.

View2 a raison_de View1

et

View3 a raison_ de View2

Après avoir défini ces règles, RelativeLayout s’occupe de tout le reste. Les animations modifient le margins (en déplacement) et le [width,height] À l'échelle

Pour accéder à chaque enfant (afin que vous puissiez le gonfler avec votre fragment, vous pouvez appeler

public FrameLayout getLeftLayout() {}

public FrameLayout getMiddleLayout() {}

public FrameLayout getRightLayout() {}

Ci-dessous sont démontrés les 2 animations


Étape 1

--- IN Screen ----------! ----- OUT ----

[Vue1] [_____ Vue2 _____] [_____ Vue3_____]

Stage2

--OUT -! -------- IN Screen ------

[Vue1] [Vue2] [_____ Vue3_____]

66
weakwire

Nous avons construit une bibliothèque appelée PanesLibrary qui résout ce problème. C'est encore plus flexible que ce qui a été offert auparavant parce que:

  • Chaque volet peut être dimensionné dynamiquement
  • Il permet un nombre quelconque de vitres (pas seulement 2 ou 3)
  • Les fragments à l'intérieur des vitres sont correctement conservés lors des changements d'orientation.

Vous pouvez le vérifier ici: https://github.com/Mapsaurus/Android-PanesLibrary

Voici une démo: http://www.youtube.com/watch?v=UA-lAGVXoLU&feature=youtu.be

Il vous permet en principe d'ajouter facilement un nombre quelconque de volets de taille dynamique et d'attacher des fragments à ces volets. J'espère que vous le trouverez utile! :)

13
coda

En vous basant sur l'un des exemples auxquels vous avez lié ( http://Android.amberfog.com/?p=758 ), que diriez-vous d'animer le layout_weight propriété? De cette façon, vous pouvez animer le changement de poids des 3 fragments ensemble, ET vous obtenez le bonus qu'ils glissent tous ensemble:

Commencez avec une mise en page simple. Puisque nous allons animer layout_weight, nous avons besoin de LinearLayout comme vue racine pour les 3 panneaux .:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/container"
Android:orientation="horizontal"
Android:layout_width="match_parent"
Android:layout_height="match_parent">

<LinearLayout
    Android:id="@+id/panel1"
    Android:layout_width="0dip"
    Android:layout_weight="1"
    Android:layout_height="match_parent"/>

<LinearLayout
    Android:id="@+id/panel2"
    Android:layout_width="0dip"
    Android:layout_weight="2"
    Android:layout_height="match_parent"/>

<LinearLayout
    Android:id="@+id/panel3"
    Android:layout_width="0dip"
    Android:layout_weight="0"
    Android:layout_height="match_parent"/>
</LinearLayout>

Puis le cours de démonstration:

public class DemoActivity extends Activity implements View.OnClickListener {
    public static final int ANIM_DURATION = 500;
    private static final Interpolator interpolator = new DecelerateInterpolator();

    boolean isCollapsed = false;

    private Fragment frag1, frag2, frag3;
    private ViewGroup panel1, panel2, panel3;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        panel1 = (ViewGroup) findViewById(R.id.panel1);
        panel2 = (ViewGroup) findViewById(R.id.panel2);
        panel3 = (ViewGroup) findViewById(R.id.panel3);

        frag1 = new ColorFrag(Color.BLUE);
        frag2 = new InfoFrag();
        frag3 = new ColorFrag(Color.RED);

        final FragmentManager fm = getFragmentManager();
        final FragmentTransaction trans = fm.beginTransaction();

        trans.replace(R.id.panel1, frag1);
        trans.replace(R.id.panel2, frag2);
        trans.replace(R.id.panel3, frag3);

        trans.commit();
    }

    @Override
    public void onClick(View view) {
        toggleCollapseState();
    }

    private void toggleCollapseState() {
        //Most of the magic here can be attributed to: http://Android.amberfog.com/?p=758

        if (isCollapsed) {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 0.0f, 1.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 1.0f, 2.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 2.0f, 0.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        } else {
            PropertyValuesHolder[] arrayOfPropertyValuesHolder = new PropertyValuesHolder[3];
            arrayOfPropertyValuesHolder[0] = PropertyValuesHolder.ofFloat("Panel1Weight", 1.0f, 0.0f);
            arrayOfPropertyValuesHolder[1] = PropertyValuesHolder.ofFloat("Panel2Weight", 2.0f, 1.0f);
            arrayOfPropertyValuesHolder[2] = PropertyValuesHolder.ofFloat("Panel3Weight", 0.0f, 2.0f);
            ObjectAnimator localObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(this, arrayOfPropertyValuesHolder).setDuration(ANIM_DURATION);
            localObjectAnimator.setInterpolator(interpolator);
            localObjectAnimator.start();
        }
        isCollapsed = !isCollapsed;
    }

    @Override
    public void onBackPressed() {
        //TODO: Very basic stack handling. Would probably want to do something relating to fragments here..
        if(isCollapsed) {
            toggleCollapseState();
        } else {
            super.onBackPressed();
        }
    }

    /*
     * Our magic getters/setters below!
     */

    public float getPanel1Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)     panel1.getLayoutParams();
        return params.weight;
    }

    public void setPanel1Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel1.getLayoutParams();
        params.weight = newWeight;
        panel1.setLayoutParams(params);
    }

    public float getPanel2Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        return params.weight;
    }

    public void setPanel2Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel2.getLayoutParams();
        params.weight = newWeight;
        panel2.setLayoutParams(params);
    }

    public float getPanel3Weight() {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        return params.weight;
    }

    public void setPanel3Weight(float newWeight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) panel3.getLayoutParams();
        params.weight = newWeight;
        panel3.setLayoutParams(params);
    }


    /**
     * Crappy fragment which displays a toggle button
     */
    public static class InfoFrag extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle     savedInstanceState) {
            LinearLayout layout = new LinearLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
            layout.setBackgroundColor(Color.DKGRAY);

            Button b = new Button(getActivity());
            b.setOnClickListener((DemoActivity) getActivity());
            b.setText("Toggle Me!");

            layout.addView(b);

            return layout;
        }
    }

    /**
     * Crappy fragment which just fills the screen with a color
     */
    public static class ColorFrag extends Fragment {
        private int mColor;

        public ColorFrag() {
            mColor = Color.BLUE; //Default
        }

        public ColorFrag(int color) {
            mColor = color;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            FrameLayout layout = new FrameLayout(getActivity());
            layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

            layout.setBackgroundColor(mColor);
            return layout;
        }
    }
}

De plus, cet exemple n'utilise pas FragmentTransactions pour réaliser les animations (il anime plutôt les vues auxquelles les fragments sont attachés); bien, cela ne semble pas être un mauvais compromis :)

Horrible vidéo basse résolution de celle-ci en action: http://youtu.be/Zm517j3bFCo

6
DanielGrech

Cela n'utilise pas de fragments ... C'est une mise en page personnalisée avec 3 enfants. Lorsque vous cliquez sur un message, vous décalez les 3 enfants à l'aide de offsetLeftAndRight() et d'un animateur.

Dans JellyBean, vous pouvez activer "Afficher les limites de la présentation" dans les paramètres "Options du développeur". Lorsque l'animation de la diapositive est terminée, vous pouvez toujours voir que le menu de gauche est toujours là, mais sous le panneau du milieu.

Cela ressemble à menu de l'application Fly-in de Cyril Mottier , mais avec 3 éléments au lieu de 2.

De plus, le ViewPager du troisième enfant est une autre indication de ce comportement: ViewPager utilise généralement des fragments (je sais qu'ils ne sont pas obligés, mais je n'ai jamais vu d'implémentation autre que Fragment), et comme vous ne pouvez pas utiliser Fragments dans un autre Fragment, les 3 enfants ne sont probablement pas des fragments ....

5

J'essaie actuellement de faire quelque chose comme ça, sauf que l'échelle Fragment B prend l'espace disponible et que le volet 3 peut être ouvert en même temps s'il y a assez de place. Voici ma solution jusqu'à présent, mais je ne suis pas sûr de pouvoir m'en tenir à cela. J'espère que quelqu'un fournira une réponse montrant The Right Way.

Au lieu d'utiliser un LinearLayout et d'animer le poids, j'utilise un RelativeLayout et j'anime les marges. Je ne suis pas sûr que ce soit la meilleure solution car elle nécessite un appel à requestLayout () à chaque mise à jour. C'est lisse sur tous mes appareils cependant.

Donc, j'anime la mise en page, je n'utilise pas de transaction de fragments. Je manipule le bouton de retour manuellement pour fermer le fragment C s'il est ouvert.

FragmentB utilise layout_toLeftOf/ToRightOf pour le maintenir aligné sur les fragments A et C.

Lorsque mon application déclenche un événement pour afficher le fragment C, j'insère le fragment C et i extrait le fragment A en même temps. (2 animations séparées). Inversement, quand le fragment A est ouvert, je ferme le C en même temps.

En mode portrait ou sur un écran plus petit, j’utilise une disposition légèrement différente et je fais glisser le fragment C sur l’écran.

Pour utiliser le pourcentage de largeur des fragments A et C, je pense que vous devriez le calculer au moment de l'exécution ... (?)

Voici la présentation de l'activité:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:id="@+id/rootpane"
    Android:layout_width="fill_parent"
    Android:layout_height="fill_parent">

    <!-- FRAGMENT A -->
    <fragment
        Android:id="@+id/fragment_A"
        Android:layout_width="300dp"
        Android:layout_height="fill_parent"
        class="com.xyz.fragA" />

    <!-- FRAGMENT C -->
    <fragment
        Android:id="@+id/fragment_C"
        Android:layout_width="600dp"
        Android:layout_height="match_parent"
        Android:layout_alignParentRight="true"
        class="com.xyz.fragC"/>

    <!-- FRAGMENT B -->
    <fragment
        Android:id="@+id/fragment_B"
        Android:layout_width="match_parent"
        Android:layout_height="fill_parent"
        Android:layout_marginLeft="0dip"
        Android:layout_marginRight="0dip"
        Android:layout_toLeftOf="@id/fragment_C"
        Android:layout_toRightOf="@id/fragment_A"
        class="com.xyz.fragB" />

</RelativeLayout>

L'animation pour glisser FragmentC dedans ou dehors:

private ValueAnimator createFragmentCAnimation(final View fragmentCRootView, boolean slideIn) {

    ValueAnimator anim = null;

    final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) fragmentCRootView.getLayoutParams();
    if (slideIn) {
        // set the rightMargin so the view is just outside the right Edge of the screen.
        lp.rightMargin = -(lp.width);
        // the view's visibility was GONE, make it VISIBLE
        fragmentCRootView.setVisibility(View.VISIBLE);
        anim = ValueAnimator.ofInt(lp.rightMargin, 0);
    } else
        // slide out: animate rightMargin until the view is outside the screen
        anim = ValueAnimator.ofInt(0, -(lp.width));

    anim.setInterpolator(new DecelerateInterpolator(5));
    anim.setDuration(300);
    anim.addUpdateListener(new AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer rightMargin = (Integer) animation.getAnimatedValue();
            lp.rightMargin = rightMargin;
            fragmentCRootView.requestLayout();
        }
    });

    if (!slideIn) {
        // if the view was sliding out, set visibility to GONE once the animation is done
        anim.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                fragmentCRootView.setVisibility(View.GONE);
            }
        });
    }
    return anim;
}
2
boblebel