如何为ViewGroups的布局属性添加动画效果?

8

I have the following layout:

<?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    |
  |                        |            |
  |                        |            |
  +------------------------+------------+

当某个切换开关被切换时,我想要将左侧的宽度动画扩展到填满整个屏幕。同时,我希望将右侧的宽度动画收缩为零。稍后,当再次切换切换开关时,我需要将事物恢复到上述状态。
我尝试编写自己的Animation,调用View.getWidth(),但是当我通过设置View.getLayoutParams().width来重新调整其大小时,它比开始时要宽。我怀疑我做错了。我也阅读了所有有关Honeycomb动画的文档,但我不想进行翻译或缩放……我想要动画布局宽度属性。我找不到这方面的例子。
正确的方法是什么?
3个回答

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

在Knickedi的精彩答案中,我想补充一点 - 以防有人需要:

如果您使用权重进行动画制作,您将遇到包含视图和视图组上的剪切/非剪切问题。特别是如果您使用带有权重的视图组作为片段容器。要解决这个问题,您可能需要动画化受影响的子视图和视图组/片段容器的边距。

而且,为了同时完成所有这些事情,最好使用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绑定在一起,以使动画看起来更加平滑。
这种方法减少了编写可运行代码和其中的权重计算的需要,但需要定义一个自定义类。

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