web-dev-qa-db-fra.com

Comment animer View.setVisibility (GONE)

Je veux créer un Animation pour quand un View obtient sa visibilité définie sur GONE. Au lieu de disparaître, le View devrait "s'effondrer". J'ai essayé ceci avec un ScaleAnimation mais ensuite le View s'est effondré, mais la disposition ne redimensionnera son espace qu'après (ou avant) le Animation s'arrête (ou démarre).

Comment puis-je créer le Animation pour que, pendant l'animation, les View inférieurs restent directement sous le contenu, au lieu d'avoir un espace vide?

84
MrSnowflake

Il ne semble pas y avoir de moyen facile de le faire via l'API, car l'animation modifie simplement la matrice de rendu de la vue, pas la taille réelle. Mais nous pouvons définir une marge négative pour tromper LinearLayout en pensant que la vue se rétrécit.

Je recommande donc de créer votre propre classe d'animation, basée sur ScaleAnimation, et de remplacer la méthode "applyTransformation" pour définir de nouvelles marges et mettre à jour la disposition. Comme ça...

public class Q2634073 extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.q2634073);
        findViewById(R.id.item1).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        view.startAnimation(new MyScaler(1.0f, 1.0f, 1.0f, 0.0f, 500, view, true));
    }

    public class MyScaler extends ScaleAnimation {

        private View mView;

        private LayoutParams mLayoutParams;

        private int mMarginBottomFromY, mMarginBottomToY;

        private boolean mVanishAfter = false;

        public MyScaler(float fromX, float toX, float fromY, float toY, int duration, View view,
                boolean vanishAfter) {
            super(fromX, toX, fromY, toY);
            setDuration(duration);
            mView = view;
            mVanishAfter = vanishAfter;
            mLayoutParams = (LayoutParams) view.getLayoutParams();
            int height = mView.getHeight();
            mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
            mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            if (interpolatedTime < 1.0f) {
                int newMarginBottom = mMarginBottomFromY
                        + (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
                mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,
                    mLayoutParams.rightMargin, newMarginBottom);
                mView.getParent().requestLayout();
            } else if (mVanishAfter) {
                mView.setVisibility(View.GONE);
            }
        }

    }

}

La mise en garde habituelle s'applique: parce que nous remplaçons une méthode protégée (applyTransformation), cela ne sera pas garanti de fonctionner dans les futures versions d'Android.

51
Andy

Mettez la vue dans une mise en page si ce n'est pas le cas et définissez Android:animateLayoutChanges="true" pour cette disposition.

98
Vinay Wadhwa

J'ai utilisé la même technique qu'Andy a présentée ici. J'ai écrit ma propre classe d'animation pour cela, qui anime la valeur de la marge, faisant disparaître/apparaître l'effet de l'élément. Cela ressemble à ceci:

public class ExpandAnimation extends Animation {

// Initializations...

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    super.applyTransformation(interpolatedTime, t);

    if (interpolatedTime < 1.0f) {

        // Calculating the new bottom margin, and setting it
        mViewLayoutParams.bottomMargin = mMarginStart
                + (int) ((mMarginEnd - mMarginStart) * interpolatedTime);

        // Invalidating the layout, making us seeing the changes we made
        mAnimatedView.requestLayout();
    }
}
}

J'ai un exemple complet qui fonctionne sur mon billet de blog http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

7
Udinic

J'ai utilisé la même technique qu'Andy ici, et je l'ai affinée pour qu'elle puisse être utilisée pour l'expansion et la réduction sans problèmes, en utilisant également une technique décrite ici: https://stackoverflow.com/a/11426510/1317564

import Android.view.View;
import Android.view.ViewTreeObserver;
import Android.view.animation.ScaleAnimation;
import Android.view.animation.Transformation;
import Android.widget.LinearLayout;

class LinearLayoutVerticalScaleAnimation extends ScaleAnimation {
    private final LinearLayout view;
    private final LinearLayout.LayoutParams layoutParams;

    private final float beginY;
    private final float endY;
    private final int originalBottomMargin;

    private int expandedHeight;
    private boolean marginsInitialized = false;
    private int marginBottomBegin;
    private int marginBottomEnd;

    private ViewTreeObserver.OnPreDrawListener preDrawListener;

    LinearLayoutVerticalScaleAnimation(float beginY, float endY,
            LinearLayout linearLayout) {
        super(1f, 1f, beginY, endY);

        this.view = linearLayout;
        this.layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();

        this.beginY = beginY;
        this.endY = endY;
        this.originalBottomMargin = layoutParams.bottomMargin;

        if (view.getHeight() != 0) {
            expandedHeight = view.getHeight();
            initializeMargins();
        }
    }

    private void initializeMargins() {
        final int beginHeight = (int) (expandedHeight * beginY);
        final int endHeight = (int) (expandedHeight * endY);

        marginBottomBegin = beginHeight + originalBottomMargin - expandedHeight;
        marginBottomEnd = endHeight + originalBottomMargin - expandedHeight;
        marginsInitialized = true;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);     

        if (!marginsInitialized && preDrawListener == null) {                       
            // To avoid glitches, don't draw until we've initialized everything.
            preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {                    
                    if (view.getHeight() != 0) {
                        expandedHeight = view.getHeight();
                        initializeMargins();
                        adjustViewBounds(0f);
                        view.getViewTreeObserver().removeOnPreDrawListener(this);                               
                    }

                    return false;
                }
            };

            view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);                   
        }

        if (interpolatedTime < 1.0f && view.getVisibility() != View.VISIBLE) {          
            view.setVisibility(View.VISIBLE);           
        }

        if (marginsInitialized) {           
            if (interpolatedTime < 1.0f) {
                adjustViewBounds(interpolatedTime);
            } else if (endY <= 0f && view.getVisibility() != View.GONE) {               
                view.setVisibility(View.GONE);
            }
        }
    }

    private void adjustViewBounds(float interpolatedTime) {
        layoutParams.bottomMargin = 
                marginBottomBegin + (int) ((marginBottomEnd - marginBottomBegin) * interpolatedTime);       

        view.getParent().requestLayout();
    }
}
2
Learn OpenGL ES