2011-09-22 95 views
8

我有以下佈局:如何動畫ViewGroups的佈局屬性?

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
       android:layout_height="match_parent" 
       android:layout_width="match_parent"> 

    <FrameLayout android:id="@+id/viewgroup_left" 
       android:layout_height="match_parent" 
       android:layout_weight="2" 
       android:layout_width="0dp"> 

     ... children ... 

    </FrameLayout> 

    <LinearLayout android:id="@+id/viewgroup_right" 
        android:layout_height="match_parent" 
        android:layout_weight="1" 
        android:layout_width="0dp" 
        android:orientation="vertical"> 

     ... children ... 

    </LinearLayout> 

</LinearLayout> 

我結束了這樣的事情:

+------------------------+------------+ 
    |      |   | 
    |      |   | 
    |   Left   | Right | 
    |      |   | 
    |      |   | 
    +------------------------+------------+ 

當某個切換被觸發,我想動畫左,使得其寬度擴大,以填補整個屏幕。同時,我想動畫Right的寬度,使其縮小到零。之後,當切換再次切換時,我需要將事物恢復到上述狀態。

我試着寫我自己的動畫,調用View.getWidth(),但當我動畫回到該值(通過設置View.getLayoutParams().width)它比它開始時更寬。我懷疑我只是做錯了。我也閱讀了Honeycomb動畫內容的所有文檔,但我不想翻譯或縮放...我想爲佈局寬度屬性設置動畫效果。我找不到這樣的例子。

這樣做的正確方法是什麼?

回答

12

因爲沒有人幫助你的呢,我的第一個答案是這樣一個爛攤子,我會盡量給你正確答案,這個時候;-)

其實我喜歡這個想法,我認爲這是一個很好的視覺效果這可能對一羣人有用。我會實現正確的視圖溢出(我認爲收縮看起來很奇怪,因爲文本正在擴大到底部)。

但無論如何,這裏的代碼完美無缺(甚至可以在動畫製作時切換)。

快速解釋
你叫toggle與你的方向一個布爾值,這將開始處理動畫調用循環。這將根據方向和過去時間增加或減少兩個視圖的權重(用於平滑計算和動畫)。只要尚未達到開始或結束位置,動畫調用循環將自行調用。

佈局:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="horizontal" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:weightSum="10" 
    android:id="@+id/slide_layout"> 
    <TextView 
     android:layout_weight="7" 
     android:padding="10dip" 
     android:id="@+id/left" 
     android:layout_width="0dip" 
     android:layout_height="fill_parent"></TextView> 
    <TextView 
     android:layout_weight="3" 
     android:padding="10dip" 
     android:id="@+id/right" 
     android:layout_width="0dip" 
     android:layout_height="fill_parent"></TextView> 
</LinearLayout> 

活動:

public class TestActivity extends Activity { 

    private static final int ANIMATION_DURATION = 1000; 

    private View mSlidingLayout; 
    private View mLeftView; 
    private View mRightView; 

    private boolean mAnimating = false; 
    private boolean mLeftExpand = true; 
    private float mLeftStartWeight; 
    private float mLayoutWeightSum; 
    private Handler mAnimationHandler = new Handler(); 
    private long mAnimationTime; 

    private Runnable mAnimationStep = new Runnable() { 
     @Override 
     public void run() { 
      long currentTime = System.currentTimeMillis(); 
      float animationStep = (currentTime - mAnimationTime) * 1f/ANIMATION_DURATION; 
      float weightOffset = animationStep * (mLayoutWeightSum - mLeftStartWeight); 

      LinearLayout.LayoutParams leftParams = (LinearLayout.LayoutParams) 
        mLeftView.getLayoutParams(); 
      LinearLayout.LayoutParams rightParams = (LinearLayout.LayoutParams) 
        mRightView.getLayoutParams(); 

      leftParams.weight += mLeftExpand ? weightOffset : -weightOffset; 
      rightParams.weight += mLeftExpand ? -weightOffset : weightOffset; 

      if (leftParams.weight >= mLayoutWeightSum) { 
       mAnimating = false; 
       leftParams.weight = mLayoutWeightSum; 
       rightParams.weight = 0; 
      } else if (leftParams.weight <= mLeftStartWeight) { 
       mAnimating = false; 
       leftParams.weight = mLeftStartWeight; 
       rightParams.weight = mLayoutWeightSum - mLeftStartWeight; 
      } 

      mSlidingLayout.requestLayout(); 

      mAnimationTime = currentTime; 

      if (mAnimating) { 
       mAnimationHandler.postDelayed(mAnimationStep, 30); 
      } 
     } 
    }; 

    private void toggleExpand(boolean expand) { 
     mLeftExpand = expand; 

     if (!mAnimating) { 
      mAnimating = true; 
      mAnimationTime = System.currentTimeMillis(); 
      mAnimationHandler.postDelayed(mAnimationStep, 30); 
     } 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.slide_test); 

     mLeftView = findViewById(R.id.left); 
     mRightView = findViewById(R.id.right); 
     mSlidingLayout = findViewById(R.id.slide_layout); 

     mLeftStartWeight = ((LinearLayout.LayoutParams) 
       mLeftView.getLayoutParams()).weight; 
     mLayoutWeightSum = ((LinearLayout) mSlidingLayout).getWeightSum(); 
    } 
} 
2

只是增加我的2美分這裏Knickedi的出色答卷 - 以防萬一有人需要它:

如果動畫使用權重你最終會遇到剪輯/非剪輯包含的視圖和視圖組的問題。如果您將視圖組與重量作爲片段容器一起使用,則尤其如此。爲了克服它,你可能需要動畫有問題的子視圖和viewgroups/fragment容器的邊界。

而且,所有這些事情做起來,它總是更好地爲不同的方式去ObjectAnimator和AnimatorSet(如果你可以使用它們),具有一定的實用工具類一起像MarginProxy

1

到發佈的解決方案@ knickedi是使用ObjectAnimator代替Runnable。這個想法是使用ObjectAnimator來調整左右視圖的權重。但是,這些視圖需要進行自定義,以便權重可以作爲ObjectAnimator動畫的屬性公開。

因此,首先,定義定製視圖(使用的LinearLayout作爲示例):

public class CustomLinearLayout extends LinearLayout { 
    public CustomLinearLayout(Context context) { 
     super(context); 
    } 

    public CustomLinearLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public CustomLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    public void setMyWeight(float value) { 
     LinearLayout.LayoutParams p = (LinearLayout.LayoutParams)getLayoutParams(); 
     p.weight = value; 
     requestLayout(); 
    } 
} 

然後,更新的佈局XML來使用此自定義線性佈局。

然後,當你需要切換動畫,使用ObjectAnimator:

ObjectAnimator rightView = ObjectAnimator.ofFloat(viewgroup_right, "MyWeight", 0.5f, 1.0f); 
ObjectAnimator leftView = ObjectAnimator.ofFloat(viewgroup_left, "MyWeight", 0.5f, 0.0f); 
AnimatorSet animatorSet = new AnimatorSet(); 
animatorSet.setDuration(1000); // 1 second of animation 
animatorSet.playTogether(rightView, leftView); 
animatorSet.start(); 

上面的代碼假定這兩種觀點都是線性佈局,並在一半重量的開始。動畫會將正確的視圖展開爲全部重量(所以左側的視圖隱藏)。請注意,ObjectAnimator使用自定義線性佈局的「MyWeight」屬性進行動畫製作。 AnimatorSet用於將左右ObjectAnimators綁定在一起,因此動畫看起來很平滑。

該方法減少了編寫可運行代碼和其中的權重計算的需求,但它需要定製的類來定義。