Come si anima View.setVisibility (GONE)


84

Voglio creare un Animationper quando Viewottiene la sua visibilità impostata su GONE. Invece di semplicemente scomparire, il Viewdovrebbe "crollare". L'ho provato con un ScaleAnimationma poi Viewè compresso, ma il layout ridimensionerà solo lo spazio dopo (o prima) le Animationfermate (o le partenze).

Come posso fare in Animationmodo che, durante l'animazione, le Views inferiori rimangano direttamente sotto il contenuto, invece di avere uno spazio vuoto?


Ho usato la stessa tecnica, come ha presentato Andy qui, sul mio ExpandAnimation: udinic.wordpress.com/2011/09/03/expanding-listview-items Non ho usato un'animazione in scala, ho solo costruito una nuova animazione classe per quello.
Udinic

Questo è stato molto utile mentre stavo cercando di farlo. Grazie
atraudes

Eccellente Udinic .. ha davvero risolto il mio problema .. :) grazie
Yousuf Qureshi

Ottimo, devo adattarmi al mio problema, ma alla fine funziona. Per me questa soluzione è stata migliore dell'altra risposta.
Derzu

Risposte:


51

Non sembra esserci un modo semplice per farlo tramite l'API, perché l'animazione cambia solo la matrice di rendering della vista, non la dimensione effettiva. Ma possiamo impostare un margine negativo per indurre LinearLayout a pensare che la vista si stia riducendo.

Quindi ti consiglio di creare la tua classe Animation, basata su ScaleAnimation, e di sovrascrivere il metodo "applyTransformation" per impostare nuovi margini e aggiornare il layout. Come questo...

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);
            }
        }

    }

}

Si applica il solito avvertimento: poiché stiamo sovrascrivendo un metodo protetto (applyTransformation), non è garantito che funzioni nelle versioni future di Android.


3
Perché diavolo non ci ho pensato ?! Grazie. Inoltre non ottengo: "Vale la solita avvertenza: poiché stiamo sovrascrivendo un metodo protetto (applyTransformation), non è garantito che funzioni nelle versioni future di Android." - Perché le funzioni protette differiscono tra le versioni API? Quelle non sono nascoste e sono implementate protette in modo che tu possa sovrascriverle (altrimenti sarebbero incluse nel pacchetto).
MrSnowflake

Probabilmente hai ragione sul metodo protetto. Tendo ad essere eccessivamente cauto nell'accedervi in ​​un'API.
Andy

1
Questo ha funzionato alla grande per me, tranne per il fatto che per far funzionare il "collasso" (da Y = 0.0f, aY = 1.0f), ho dovuto rimuovere il 0 - nel marginBottomToYcalcolo.
dmon

2
Suggerirei di utilizzare il tipo generico MarginLayoutParamsinvece di trasmetterlo a un LayoutParamtipo specifico .
Paul Lammertsma

3
Supponiamo di dover attivare l'animazione, quindi come potrei farlo al contrario. Per favore consiglio.
Umesh

99

Metti la vista in un layout se non lo è e impostala android:animateLayoutChanges="true"per quel layout.


1
L'API minima richiesta è 11 o superiore! Non è possibile utilizzare questo metodo per la versione inferiore.
Nagaraj Alagusudaram

2
questo è l'attributo di layout più sottovalutato ... grazie!
Andres Santiago

7

Ho usato la stessa tecnica che Andy ha presentato qui. Per questo ho scritto la mia classe Animation, che anima il valore del margine, facendo scomparire / apparire l'effetto dell'elemento. Assomiglia a questo:

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();
    }
}
}

Ho un esempio completo che funziona sul mio post del blog http://udinic.wordpress.com/2011/09/03/expanding-listview-items/


2

Ho usato la stessa tecnica di Andy qui e l'ho perfezionata in modo che possa essere utilizzata per espandere e collassare senza problemi, anche usando una tecnica descritta qui: 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();
    }
}

È possibile utilizzarlo per comprimere prima un LinearLayout esistente e quindi espandere nuovamente lo stesso LinearLayout in seguito? Quando provo a farlo, collassa e non si espande di nuovo (probabilmente perché l'altezza della vista ora è 0 o qualcosa del genere).
AHaahr

Ho scoperto che funziona in modo più affidabile quando il layout lineare contiene più di una vista. Se contiene solo una vista, non si espanderà sempre.
Impara OpenGL ES
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.