2010-04-14 31 views
82

我想在View的可見性設置爲GONE時設置AnimationView不應該消失,而應該「崩潰」。我嘗試了ScaleAnimation,但View已崩潰,但佈局將僅在Animation停止(或開始)之後(或之前)調整其空間大小。如何設置View.setVisibility的動畫

我該如何製作Animation,以便在動畫製作時,較低的View將保持在內容的正下方,而不是空白區域?

+0

我已經使用了與Andy在這裏展示的技術相同的技術,在我的ExpandAnimation上: http://udinic.wordpress.com/2011/09/03/expanding-listview-items/我沒有使用我只是爲此構建了一個新的Animation類。 – Udinic 2011-09-03 22:14:15

+0

這是非常有用的,當我試圖做到這一點。謝謝 – atraudes 2011-09-23 19:42:49

+0

優秀的Udinic ..真的解決了我的問題.. :)謝謝 – 2011-11-22 14:20:46

回答

51

似乎沒有一種簡單的方法可以通過API實現此目的,因爲動畫只會改變視圖的渲染矩陣,而不會改變實際大小。但是我們可以設置一個負邊界來讓LinearLayout認爲視圖變小。

所以我建議根據ScaleAnimation創建您自己的Animation類,並重寫「applyTransformation」方法來設置新的邊距並更新佈局。像這樣...

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

    } 

} 

通常的原則同樣適用:因爲我們覆蓋一個受保護的方法(applyTransformation),並不保證在Android的未來版本的工作。

+3

爲什麼我沒有想到這個?謝謝。我也沒有得到:「通常的警告適用:因爲我們正在覆蓋受保護的方法(applyTransformation),所以這並不能保證在將來的Android版本中有效。」 - 爲什麼API版本的受保護功能會有所不同?這些不是隱藏的,並且實現了保護,以便您可以覆蓋它們(否則它們將成爲程序包範圍)。 – MrSnowflake 2010-10-18 17:16:16

+0

對於受保護的方法您可能是正確的。我傾向於在API中訪問它們過於謹慎。 – Andy 2010-10-18 21:42:09

+0

Atleast那麼你很確定API更新不會破壞你的應用程序:)。 – MrSnowflake 2010-10-19 13:53:05

6

我用了和Andy一樣的技巧。我爲此編寫了自己的動畫類,使邊距的值變爲動畫,從而使物品的效果消失/出現。 它看起來像這樣:

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

我有一個完整的例子,在我的博客文章作品 http://udinic.wordpress.com/2011/09/03/expanding-listview-items/

2

我用同樣的技術,因爲安迪在這裏,和完善它,以便它可以用於擴大和摺疊無毛刺,也利用這裏所描述的技術: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

是否可以使用它來先摺疊現有的LinearLayout,然後再次展開同一個LinearLayout?當我嘗試這樣做時,它會崩潰並且不會再次展開(可能是因爲視圖的高度現在爲0或類似的值)。 – AHaahr 2013-06-27 21:50:54

+0

當線性佈局包含多個視圖時,我發現它可以更可靠地工作。如果它只包含一個視圖,那麼它不會一直展開。 – 2013-08-13 19:58:19

94

把視圖中的佈局,如果它不是,並設置android:animateLayoutChanges="true"該佈局。

+1

最低API要求是11或更高!對於較低版本,不能使用此方法。 – 2014-10-19 07:06:04

+1

你好Mr.Life :) – 2015-04-22 11:24:43

+2

這是最被低估的佈局屬性......謝謝! – 2016-01-15 21:56:36