2010-04-14 4 views
82

Je souhaite créer un Animation lorsqu'un View obtient sa visibilité définie sur GONE. Au lieu de simplement dissapearing, le View devrait «s'effondrer». J'ai essayé ceci avec un ScaleAnimation mais alors le View est effondrement, mais la disposition redimensionnera seulement son espace après (ou avant) les arrêts Animation (ou commence).Comment animer View.setVisibility (GONE)

Comment puis-je faire le Animation de sorte que, pendant l'animation, les View inférieurs restent au-dessous du contenu, au lieu d'avoir un espace vide?

+0

J'ai utilisé la même technique, comme Andy ici a présenté, sur mon ExpandAnimation: http://udinic.wordpress.com/2011/09/03/expanding-listview-items/ Je n'ai pas utilisé un animation d'échelle, je viens de construire une nouvelle classe d'animation pour cela. – Udinic

+0

C'était très utile pendant que j'essayais de faire ça. Merci – atraudes

+0

Excellent Udinic .. vraiment résolu mon problème .. :) merci –

Répondre

51

Il ne semble pas y avoir un 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 diminue.

Je vous recommande donc de créer votre propre classe Animation, basée sur ScaleAnimation, et de surcharger la méthode "applyTransformation" pour définir de nouvelles marges et mettre à jour la mise en page. 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 applique: parce que nous prépondérants une méthode protégée (applyTransformation), ce n'est pas garanti pour fonctionner dans les futures versions d'Android.

+3

Pourquoi diable n'ai-je pas pensé à ça ?! Je vous remercie. Je n'obtiens pas le: "La mise en garde habituelle s'applique: parce que nous sommes en train de surcharger une méthode protégée (applyTransformation), ceci n'est pas garanti pour fonctionner dans les futures versions d'Android." - Pourquoi les fonctions protégées diffèrent-elles entre les versions d'API? Ceux-ci ne sont pas cachés et sont implémentés protégés afin que vous puissiez les remplacer (sinon ils auraient une portée de paquet). – MrSnowflake

+0

Vous avez probablement raison sur la méthode protégée. J'ai tendance à être trop prudent quant à leur accès dans une API. – Andy

+0

Au moins alors vous êtes sûr que les mises à jour de l'API ne vous détruiront pas les applications :). – MrSnowflake

6

J'ai utilisé la même technique qu'Andy a présenté ici. J'ai écrit ma propre classe Animation pour ça, qui anime la valeur de la marge, faisant disparaître/apparaître l'effet de l'objet. Il 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 blog http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

2

J'ai utilisé la même technique que Andy ici, et affiné pour qu'il puisse être utilisé pour l'expansion et l'effondrement sans pépins, 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(); 
    } 
} 
+0

Est-il possible d'utiliser ceci pour d'abord réduire un LinearLayout existant et ensuite développer le même LinearLayout après? Quand j'essaye de faire ceci, il s'effondre juste et ne s'étendra pas encore (probablement parce que la taille de la vue est maintenant 0 ou quelque chose comme cela). – AHaahr

+0

J'ai trouvé que cela fonctionne de manière plus fiable lorsque la disposition linéaire contient plus d'une vue. Si elle ne contient qu'une seule vue, elle ne s'étendra pas toujours. –

94

Mettez la vue dans une mise en page si ce n'est pas et mis android:animateLayoutChanges="true" pour cette mise en page.

+1

L'API minimale requise est 11 ou plus! Impossible d'utiliser cette méthode pour la version inférieure. –

+1

Bonjour M. Vie :) –

+2

c'est l'attribut de mise en page le plus sous-estimé ... merci! –