web-dev-qa-db-fra.com

Gardez la vue animée à la position finale de l'animation

J'anime donc une vue en utilisant l'animation:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:interpolator="@Android:anim/accelerate_interpolator" >

    <translate
        Android:duration="1000"
        Android:fromYDelta="0%"
        Android:toYDelta="-75%" />

</set>

Il fait visuellement ce que je veux mais moi aussi je veux figer le résultat de cette animation. Comme j'ai besoin d'une manière ou d'une autre pour obtenir les paramètres de mise en page résultants (ou autre chose?) Après l'animation et le définir sur la mise en page. Supposons que la disposition Y-coord passe de 0 à 100 mais je ne sais pas vraiment ce qui commençait les accords et quels sont les accords résultants. J'ai besoin d'obtenir les accords résultants et de le mettre sur la disposition car sinon, la disposition revient à sa position initiale après l'animation, mais je dois la faire rester dans sa nouvelle position au lieu de revenir. J'ai également besoin d'une solution compatible 2.3.x.
MISE À JOUR 1
J'ai aussi essayé NineOldAndroids:

ObjectAnimator.ofFloat(rootWrapper, "translationY", -rootWrapper.getHeight()*75/100).start();

cela anime la vue et la vue reste à la position animée - c'est exactement ce que je veux réaliser.
Cependant, tous les contrôles restent virtuellement à leur position. Même la mise en page n'est visible que par 25% de sa partie inférieure et 75% de l'écran de repos semble vide si j'appuie sur cette zone d'écran vide puis sur le bouton (qui était là l'animation b4) onClick déclenché. La même chose se produit avec xml-animtaions si défini

animation.setFillAfter(true); 

Comment résoudre ce problème? Tous les contrôles doivent se déplacer avec la vue.
MISE À JOUR 2
Le code:

    public void showAbout(){

        final Animation animShow = AnimationUtils.loadAnimation(this, R.anim.about_in_from_bottom);
        animShow.setFillAfter(true); 

        //ObjectAnimator.ofFloat(rootWrapper, "translationY", -rootWrapper.getHeight()*75/100).start();

        animShow.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {}

            @Override
            public void onAnimationEnd(Animation animation) {
                LinearLayout.LayoutParams rootlp = (LinearLayout.LayoutParams) rootWrapper.getLayoutParams();
                Log.i(this,"rootlp: h: "+rootlp.topMargin+ " / w: "+rootlp.bottomMargin);
                rootlp.topMargin = -rootlp.height*75/100;
                rootWrapper.setLayoutParams(rootlp);
            }
        });

        rootWrapper.startAnimation(animShow);
    }

J'ai eu le problème - rootlp.height Renvoie 0 cuz comme MATCH_PARENT ou comme. Il ne renverra pas les vrais pixels mais une certaine valeur représentant une constante! Il semble donc que j'ai 2 jeux avec .getViewTreeObserver().addOnGlobalLayoutListener.
D'accord, j'ai obtenu la hauteur réelle de rootWrapper via getViewTreeObserver (). Et maintenant, en utilisant la formule rootlp.topMargin = -rootlp.height*75/100;, J'ai encore plus de problèmes.
1) Après l'animation, la vue monte en plus (en raison du réglage LP).
2) Les contrôles contenus dans la vue animée (boutons devant remonter avec la vue parent) restent virtuellement à leur position (ils sont invisibles mais cliquables)! Visuellement, ces commandes sont déplacées vers le haut avec la vue animée et je ne peux pas l'atteindre (elle disparaît de l'écran). Mais si j'appuie sur la zone où ils se trouvaient avant l'animation correspondant aux déclencheurs onClick ()! Quel plaisir!
MISE À JOUR
Enfin, j'avais atteint mon objectif! Le problème était de déplacer les conteneurs enfants de View. Je pensais que si je déplace une vue, toutes ses vues enfant se déplacent également. Mais ce n'est pas le cas et j'ai dû déplacer manuellement les vues enfant après la fin de l'animation pour corriger les boutons fantômes.

21
Stan

Veuillez considérer setFillAfter (boolean); le mettre à true fera que la vue restera à la position animée.

Animation anim = new TranslateAnimation(0, 0, 0, 100);

// or like this
Animation anim = AnimationUtils.loadAnimation(this, R.anim.animation_name);

anim.setFillAfter(true); 
anim.setDuration(1000);

Cela peut être fait encore plus facilement en utilisant ViewPropertyAnimator, disponible à partir de l'API 14 et au-delà:

int animationpos = 500;
View.animate().y(animationpos).setDuration(1000); // this will also keep the view at the animated position

Ou considérez un AnimationListener pour obtenir les LayoutParams exactement après l'animation:

anim.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {}

            @Override
            public void onAnimationRepeat(Animation animation) {}

            @Override
            public void onAnimationEnd(Animation animation) {
                  // get layoutparams here and set it in this example your views 
                  // parent is a relative layout, please change that to your desires

                  RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) View.getLayoutParams();
                  lp.topMargin = yournewtopmargin // use topmargin for the y-property, left margin for the x-property of your view
                  View.setLayoutParams(lp);
                }
            });

    View.startAnimation(anim);

MISE À JOUR:

Comment savoir combien de pixels la vue a déplacés, selon les valeurs en pourcentage de l'animation?

La valeur en pourcentage dans votre animation .xml est relative aux dimensions de la vue animée (telles que la largeur et la hauteur). Par conséquent, il vous suffit d'utiliser les propriétés de largeur et de hauteur de votre vue (selon la météo, utilisez l'animation x ou y pour obtenir la distance d'animation réelle en pixels).

Par exemple:

Votre vue a une largeur de 400 pixels. Votre animation anime la propriété x de 0% à 75%, ce qui signifie que votre vue se déplacera de 300 pixels (400 * 0,75) vers la droite. Ainsi, onAnimationEnd (), définissez LayoutParams.leftMargin sur 300 et votre vue aura la position souhaitée.

49
Philipp Jahoda

Je trouve que la simple définition de fillEnabled et fillAfter sur TRUE ne résout pas le problème lorsque des éléments tels que les écouteurs click/touch sont impliqués. La définition du remplissage activé et du remplissage après pour être vrai garantit simplement que les pixels de la vue sont DRAWN au bon endroit final. Cependant, supposons que la vue en cours d'animation soit associée à un écouteur onclick. Cliquer sur la vue dans sa nouvelle position visible ne fera rien, mais cliquer sur l'écran dans la position où la vue était à l'origine déclenchera le onClick.

Voici un exemple de la façon de contourner ce problème:

Supposons que nous ayons une vue avec un onClickListener défini. Supposons également que les limites des vues soient 0, 0, 0, 0 (gauche, haut, droite, bas). Supposons maintenant que nous souhaitons déplacer la vue vers la droite de 200. Le code suivant montrera comment procéder de manière à déplacer les pixels à dessiner et l'objet de vue sous-jacent à la position correcte, ajustant ainsi le onClick à être appelé à partir de la bonne position.

private Animation generateMoveRightAnimation() {
    final Animation animation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 200,
            Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0);
    animation.setDuration(300);
    animation.setFillAfter(true);
    animation.setFillEnabled(true);

    animation.setAnimationListener(new Animation.AnimationListener() {
        @Override
        public void onAnimationStart(Animation animation) {

        }

        @Override
        public void onAnimationEnd(Animation animation) {
            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
            // change the coordinates of the view object itself so that on click listener reacts to new position
            view.layout(view.getLeft()+200, view.getTop(), view.getRight()+200, view.getBottom());
            repeatLevelSwitch.clearAnimation();
        }

        @Override
        public void onAnimationRepeat(Animation animation) {

        }
    });

    return animation;
}
2
TomTaila

Vous pouvez utiliser la bibliothèque proxy NineOldAndroid pour utiliser ObjectAnimator comme vous le souhaitez. il prend en charge les niveaux d'api inférieurs et utilise la bibliothèque native pour les niveaux d'api plus récents.

Importez simplement en tant que LibraryProject et tout ira bien.

0
Ercan