2010-04-14 33 views
82

Chcę zrobić Animation, gdy View ustawia widoczność na . Zamiast znikać, View powinno się "zwinąć". Próbowałem tego z ScaleAnimation, ale potem View jest zwinięty, ale układ zmieni tylko rozmiar jego przestrzeni po (lub wcześniej) zatrzymaniu (lub uruchomieniu) tej Animation.Jak animować View.setVisibility (GONE)?

Jak ustawić Animation w taki sposób, aby podczas animowania niższe View s pozostały bezpośrednio pod zawartością, zamiast pustego miejsca?

+0

Użyłem tej samej techniki, co Andy przedstawił tutaj na moim ExpandAnimation: http://udinic.wordpress.com/2011/09/03/expanding-listview-items/ Nie użyłem skala animacji, właśnie zbudowałem dla tego nową klasę animacji. – Udinic

+0

To było bardzo przydatne, gdy próbowałem to zrobić. Dziękuję – atraudes

+0

Doskonały Udinic .. Naprawdę rozwiązany mój problem .. :) dzięki –

Odpowiedz

51

Nie wydaje się, że można to łatwo zrobić za pomocą interfejsu API, ponieważ animacja zmienia tylko matrycę renderowania widoku, a nie rzeczywisty rozmiar. Ale możemy ustawić ujemny margines, aby oszukać LinearLayout, aby myśleć, że widok jest coraz mniejszy.

Zalecam więc utworzenie własnej klasy animacji opartej na ScaleAnimation i nadpisanie metody "applyTransformation" w celu ustawienia nowych marginesów i zaktualizowania układu. Tak ...

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

    } 

} 

Zwykle zastrzeżenie dotyczy: bo są nadrzędne chronioną metodę (applyTransformation), to nie jest gwarantowane do pracy w przyszłych wersjach Androida.

+3

Dlaczego, do diabła, nie pomyślałem o tym ?! Dziękuję Ci. Nie dostaję również: "Zwykłe zastrzeżenie dotyczy: ponieważ nadpisujemy chronioną metodę (applyTransformation), nie ma gwarancji, że będzie działać w przyszłych wersjach Androida." - Dlaczego funkcje chronione różnią się między wersjami API? Nie są one ukryte i są zaimplementowane chronione, aby można je było przesłonić (w przeciwnym razie byłyby one w pakietach). – MrSnowflake

+0

Prawdopodobnie masz rację co do metody chronionej. Mam tendencję do nadmiernej ostrożności w uzyskiwaniu dostępu do nich w interfejsie API. – Andy

+0

Co więcej, jesteś pewien, że aktualizacje API nie zniszczą twoich aplikacji :). – MrSnowflake

6

Użyłem tej samej techniki, którą zaprezentował tutaj Andy. Napisałem do tego własną klasę animacji, która animuje wartość marginesu, powodując zniknięcie/pojawienie się efektu elementu. Wygląda to tak:

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

Mam pełną przykład, który działa na moim blogu http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

2

użyłem tej samej techniki jak Andy tutaj, a rafinowany tak, że może on być stosowany do rozszerzania i zawaleniem bez trzasków, również za pomocą techniki opisanej tutaj: 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

Czy można użyć tego, aby najpierw zwinąć istniejący LinearLayout, a następnie ponownie rozwinąć ten sam LinearLayout? Kiedy próbuję to zrobić, po prostu zwija się i nie rozszerzy się ponownie (prawdopodobnie dlatego, że wysokość widoku wynosi teraz 0 lub coś w tym stylu). – AHaahr

+0

Stwierdziłem, że działa on bardziej niezawodnie, gdy układ liniowy zawiera więcej niż jeden widok. Jeśli zawiera tylko jeden widok, nie zawsze się rozwija. –

94

Umieść widok w układzie, jeśli to nie jest i ustawić android:animateLayoutChanges="true" dla tego układu.

+1

Minimalne wymagane API to 11 lub więcej! Nie można użyć tej metody w przypadku wersji niższej. –

+1

hello Mr. Life :) –

+2

to najbardziej niedoceniany atrybut układu ... dziękuję! –