2011-09-22 93 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绑定在一起,因此动画看起来很平滑。

该方法减少了编写可运行代码和其中的权重计算的需求,但它需要定制的类来定义。