【问题标题】:How does one Animate Layout properties of ViewGroups?ViewGroups 的 Animate Layout 属性如何?
【发布时间】:2011-11-23 02:02:31
【问题描述】:

我有以下布局:

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

当某个切换被切换时,我想为 Left 设置动画,使其宽度扩大以填满整个屏幕。同时,我想为 Right 的宽度设置动画,使其缩小为零。稍后,当再次切换切换时,我需要将东西恢复到上述状态。

我尝试编写自己的动画调用View.getWidth(),但是当我动画回到那个值时(通过设置View.getLayoutParams().width)它比开始时更宽。我怀疑我只是做错了。我还阅读了有关 Honeycomb 动画内容的所有文档,但我不想翻译或缩放...我想为布局宽度属性设置动画。我找不到这样的例子。

这样做的正确方法是什么?

【问题讨论】:

    标签: android animation viewgroup android-3.1-honeycomb


    【解决方案1】:

    由于还没有人帮助过你,而且我的第一个答案太混乱了,这次我会尽量给你正确的答案;-)

    实际上我喜欢这个想法,我认为这是一个很棒的视觉效果,可能对一群人有用。我会实现右视图的溢出(我认为收缩看起来很奇怪,因为文本正在扩展到底部)。

    但无论如何,这里的代码工作得非常好(你甚至可以在动画时切换)。

    快速解释
    您调用 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 的出色答案中 - 以防万一有人需要:

      如果您使用权重进行动画处理,您最终会在包含的视图和视图组上遇到剪裁/非剪裁问题。如果您将具有权重的视图组用作片段容器,则尤其如此。为了克服它,您可能还需要为有问题的子视图和视图组/片段容器的边距设置动画。

      而且,要一起做所有这些事情,最好使用 ObjectAnimator 和 AnimatorSet(如果你可以使用它们),以及一些实用程序类,如 MarginProxy

      【讨论】:

        【解决方案3】:

        @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 用于将左右 ObjectAnimator 绑定在一起,因此动画看起来很流畅。

        这种方法减少了编写可运行代码和在其中计算权重的需要,但它需要定义一个自定义类。

        【讨论】:

          猜你喜欢
          • 2012-06-21
          • 1970-01-01
          • 2012-02-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-20
          • 2023-04-03
          相关资源
          最近更新 更多