如何为View.setVisibility(GONE)设置动画


84

我想做一个AnimationView它的可见性设置为GONE。不仅仅是消失,而是View应该“崩溃”。我用a尝试了一下,ScaleAnimation但随后是Viewis折叠,但是布局只会在Animation停止(或开始)之后(或之前)调整其空间的大小。

如何制作Animation动画,使小号Views保持在内容的正下方,而不是留有空白?


我在我的ExpandAnimation中使用了与Andy相同的技术:udinic.wordpress.com/2011/09/03/expanding-listview-items我没有使用比例动画,我只是构建了一个新的Animation该类。
Udinic 2011年

在我尝试执行此操作时,这非常有用。谢谢
atraudes

优秀的Udinic ..真的解决了我的问题.. :)感谢
Yousuf Qureshi

太好了,我需要适应我的问题,但最后还是行得通。对我来说,这种解决方案比其他答案更好。
Derzu'4

Answers:


51

通过API似乎没有一种简便的方法,因为动画只会更改视图的渲染矩阵,而不是实际大小。但是我们可以设置一个负裕度,以使LinearLayout认为视图越来越小。

因此,我建议基于ScaleAnimation创建自己的Animation类,并覆盖“ applyTransformation”方法以设置新的边距并更新布局。像这样...

public class Q2634073 extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.q2634073);
        findViewById(R.id.item1).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        view.startAnimation(new MyScaler(1.0f, 1.0f, 1.0f, 0.0f, 500, view, true));
    }

    public class MyScaler extends ScaleAnimation {

        private View mView;

        private LayoutParams mLayoutParams;

        private int mMarginBottomFromY, mMarginBottomToY;

        private boolean mVanishAfter = false;

        public MyScaler(float fromX, float toX, float fromY, float toY, int duration, View view,
                boolean vanishAfter) {
            super(fromX, toX, fromY, toY);
            setDuration(duration);
            mView = view;
            mVanishAfter = vanishAfter;
            mLayoutParams = (LayoutParams) view.getLayoutParams();
            int height = mView.getHeight();
            mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
            mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
        }

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            if (interpolatedTime < 1.0f) {
                int newMarginBottom = mMarginBottomFromY
                        + (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
                mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,
                    mLayoutParams.rightMargin, newMarginBottom);
                mView.getParent().requestLayout();
            } else if (mVanishAfter) {
                mView.setVisibility(View.GONE);
            }
        }

    }

}

通常需要注意的是:由于我们要覆盖一个受保护的方法(applyTransformation),因此不能保证在将来的Android版本中可以使用此方法。


3
为什么我没想到这个?!谢谢。我也没有得到:“通常需要注意的是:由于我们要覆盖一个受保护的方法(applyTransformation),因此不能保证在将来的Android版本中可以使用此方法。” -为什么受保护的函数在API版本之间会有所不同?那些不是隐藏的,并且实现了保护,因此您可以覆盖它们(否则它们将在程序包范围内)。
Mrsnowflake

您可能对受保护的方法是正确的。对于在API中访问它们,我往往过于谨慎。
安迪

1
这对我来说非常有效,除了要使“崩溃”起作用(fromY = 0.0f,toY = 1.0f)外,我必须0 - marginBottomToY计算中删除。
dmon 2011年

2
我建议使用通用类型,MarginLayoutParams而不是将其强制转换为特定LayoutParam类型。
Paul Lammertsma

3
假设我必须进行切换动画,然后如何反向进行。请指教。
Umesh 2015年

99

如果不是,则将视图放在一个布局中并android:animateLayoutChanges="true"为该布局设置。


1
要求的最低API为11或更高!无法将此方法用于较低版本。
Nagaraj Alagusudaram 2014年

2
这是最被低估的布局属性...谢谢!
Andres Santiago

7

我使用了与Andy此处介绍的相同的技术。为此,我编写了自己的动画类,该动画使边距的值动画化,从而导致该项目的效果消失/显示。看起来像这样:

public class ExpandAnimation extends Animation {

// Initializations...

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    super.applyTransformation(interpolatedTime, t);

    if (interpolatedTime < 1.0f) {

        // Calculating the new bottom margin, and setting it
        mViewLayoutParams.bottomMargin = mMarginStart
                + (int) ((mMarginEnd - mMarginStart) * interpolatedTime);

        // Invalidating the layout, making us seeing the changes we made
        mAnimatedView.requestLayout();
    }
}
}

我有一个完整的示例可用于我的博客文章 http://udinic.wordpress.com/2011/09/03/expanding-listview-items/


2

我在这里使用了与Andy相同的技术,并对它进行了改进,使其可以用于扩展和折叠而不会出现小故障,也使用此处描述的技术:https : //stackoverflow.com/a/11426510/1317564

import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;

class LinearLayoutVerticalScaleAnimation extends ScaleAnimation {
    private final LinearLayout view;
    private final LinearLayout.LayoutParams layoutParams;

    private final float beginY;
    private final float endY;
    private final int originalBottomMargin;

    private int expandedHeight;
    private boolean marginsInitialized = false;
    private int marginBottomBegin;
    private int marginBottomEnd;

    private ViewTreeObserver.OnPreDrawListener preDrawListener;

    LinearLayoutVerticalScaleAnimation(float beginY, float endY,
            LinearLayout linearLayout) {
        super(1f, 1f, beginY, endY);

        this.view = linearLayout;
        this.layoutParams = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();

        this.beginY = beginY;
        this.endY = endY;
        this.originalBottomMargin = layoutParams.bottomMargin;

        if (view.getHeight() != 0) {
            expandedHeight = view.getHeight();
            initializeMargins();
        }
    }

    private void initializeMargins() {
        final int beginHeight = (int) (expandedHeight * beginY);
        final int endHeight = (int) (expandedHeight * endY);

        marginBottomBegin = beginHeight + originalBottomMargin - expandedHeight;
        marginBottomEnd = endHeight + originalBottomMargin - expandedHeight;
        marginsInitialized = true;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);     

        if (!marginsInitialized && preDrawListener == null) {                       
            // To avoid glitches, don't draw until we've initialized everything.
            preDrawListener = new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {                    
                    if (view.getHeight() != 0) {
                        expandedHeight = view.getHeight();
                        initializeMargins();
                        adjustViewBounds(0f);
                        view.getViewTreeObserver().removeOnPreDrawListener(this);                               
                    }

                    return false;
                }
            };

            view.getViewTreeObserver().addOnPreDrawListener(preDrawListener);                   
        }

        if (interpolatedTime < 1.0f && view.getVisibility() != View.VISIBLE) {          
            view.setVisibility(View.VISIBLE);           
        }

        if (marginsInitialized) {           
            if (interpolatedTime < 1.0f) {
                adjustViewBounds(interpolatedTime);
            } else if (endY <= 0f && view.getVisibility() != View.GONE) {               
                view.setVisibility(View.GONE);
            }
        }
    }

    private void adjustViewBounds(float interpolatedTime) {
        layoutParams.bottomMargin = 
                marginBottomBegin + (int) ((marginBottomEnd - marginBottomBegin) * interpolatedTime);       

        view.getParent().requestLayout();
    }
}

是否可以使用它来首先折叠现有的LinearLayout,然后再展开相同的LinearLayout?当我尝试执行此操作时,它只会折叠而不会再次扩展(可能是因为视图的高度现在为0或类似的值)。
AHaahr 2013年

我发现当线性布局包含多个视图时,它的工作更加可靠。如果仅包含一个视图,则它不会总是扩展。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.