如何以编程方式动画化线性布局的权重属性?

23

我有一个线性布局中有两个视图,我以编程方式更改它们的layout_weight属性。是否有一种方法可以动画地改变它们的权重,使得当权重发生变化时,视图向新大小滑动?

5个回答

36

您可以简单地使用ObjectAnimator。

ObjectAnimator anim = ObjectAnimator.ofFloat(
                viewToAnimate,
                "weight",
                startValue,
                endValue);
        anim.setDuration(2500);
        anim.start();

唯一的问题是View类没有setWeight()方法(ObjectAnimator所需)。 为了解决这个问题,我编写了一个简单的包装器,帮助实现视图权重动画。

public class ViewWeightAnimationWrapper {
    private View view;

    public ViewWeightAnimationWrapper(View view) {
        if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
            this.view = view;
        } else {
            throw new IllegalArgumentException("The view should have LinearLayout as parent");
        }
    }

    public void setWeight(float weight) {
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
        params.weight = weight;
        view.getParent().requestLayout();
    }

    public float getWeight() {
        return ((LinearLayout.LayoutParams) view.getLayoutParams()).weight;
    }
}

使用方法如下:

    ViewWeightAnimationWrapper animationWrapper = new ViewWeightAnimationWrapper(view);
    ObjectAnimator anim = ObjectAnimator.ofFloat(animationWrapper,
                    "weight",
                    animationWrapper.getWeight(),
                    weight);
            anim.setDuration(2500);
            anim.start();

感谢 view.getParent().requestLayout(); - CoolMind
需要在 ViewWeightAnimationWrapper.setWeight 方法上添加 @Keep,否则在 Android Studio 3.4 中会出现警告。 - Amos
来自Swift/iOS的开发者,你需要编写如此多的代码来更新一个属性,这完全是疯狂的。我立刻就会想:“一定有其他方法。” - Robert Gummesson

5

注意:我不确定这是最好的方法,但我尝试了并且它运行良好

仅使用ValueAnimator即可。

ValueAnimator m1 = ValueAnimator.ofFloat(0.2f, 0.5f); //fromWeight, toWeight
m1.setDuration(400);
m1.setStartDelay(100); //Optional Delay
m1.setInterpolator(new LinearInterpolator());
m1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
             ((LinearLayout.LayoutParams) viewToAnimate.getLayoutParams()).weight = (float) animation.getAnimatedValue();
             viewToAnimate.requestLayout();
         }

});
m1.start();

更多关于 ValueAnimator 的信息


4

我也一直在研究这个问题。最终,我通过动画化父视图的weightsum属性来解决它,如果你在LinearLayout中有两个视图,这种方法非常好用。

参见:使用ObjectAnimator动画化weightSum属性

在下面的示例中,如果你将weightSum从1.0动画到2.0,屏幕2将会很好地出现动画效果。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dual_pane"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:weightSum="1.0">

<!-- Screen 1 -->
<LinearLayout
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:background="#ff0000"
    android:layout_weight="1">
</LinearLayout>

<!-- Screen 2 -->
<LinearLayout
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:background="#ff6600"
    android:layout_weight="1">
</LinearLayout>
</LinearLayout>

1
这种方法有一个限制,即您无法将第一个屏幕平滑地动画化为0权重。您需要一个较大的weightSum来实现这一点,但那样动画就会更快。 - headsvk
有没有办法反过来做?让左侧视图消失和重新出现的动画效果? - Gutyn

0

另一种方法是使用旧的Animation类,如https://dev59.com/5WMl5IYBdhLWcg3w5aeQ#20334557所述。在这种情况下,您可以同时更改多个视图的权重。

private static class ExpandAnimation extends Animation {
    private final View[] views;
    private final float startWeight;
    private final float deltaWeight;

    ExpandAnimation(View[] views, float startWeight, float endWeight) {
        this.views = views;
        this.startWeight = startWeight;
        this.deltaWeight = endWeight - startWeight;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float weight = startWeight + (deltaWeight * interpolatedTime);
        for (View view : views) {
            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) view.getLayoutParams();
            lp.weight = weight;
            view.setLayoutParams(lp);
        }
        views[0].getParent().requestLayout();
    }

    @Override
    public boolean willChangeBounds() {
        return true;
    }
}

如何使用那个类?谢谢。 - famfamfam
@famfamfam,你可以在以下链接中查看示例:https://dev59.com/5WMl5IYBdhLWcg3w5aeQ#20334557,https://coderwall.com/p/35xi3w/layout-change-animations-sliding-height,https://gist.github.com/rafali/5146957。但是现在有更强大的动画效果。 - CoolMind

0
以上所有答案对我都没有用(它们只是“弹”而不是动画),但在我将 weight_sum="1" 添加到父布局后,它开始工作了。以防其他人遇到相同的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接