假设我有一个带有

[v1]
[v2]


的垂直linearLayout,默认情况下v1的可见性= GONE。我想显示带有扩展动画的v1,并同时按下v2。

我尝试了以下操作:

Animation a = new Animation()
{
    int initialHeight;

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final int newHeight = (int)(initialHeight * interpolatedTime);
        v.getLayoutParams().height = newHeight;
        v.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        initialHeight = height;
    }

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


但是有了这个解决方案,动画开始时我眨了眨眼。我认为这是由v1在应用动画之前显示完整尺寸引起的。

使用javascript,这是jQuery的一行!使用android进行此操作的任何简单方法?

评论

这个问题的最高答案仍然是闪光灯的相同问题

#1 楼

我看到这个问题开始流行,所以我发布了实际的解决方案。主要优点是您不必知道扩展的高度即可应用动画,并且一旦视图被扩展,如果内容更改,视图就会适应高度。

public static void expand(final View v) {
    int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
    int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
    final int targetHeight = v.getMeasuredHeight();

    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            v.getLayoutParams().height = interpolatedTime == 1
                    ? LayoutParams.WRAP_CONTENT
                    : (int)(targetHeight * interpolatedTime);
            v.requestLayout();
        }

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

    // Expansion speed of 1dp/ms
    a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if(interpolatedTime == 1){
                v.setVisibility(View.GONE);
            }else{
                v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                v.requestLayout();
            }
        }

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

    // Collapse speed of 1dp/ms
    a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}


正如@Jefferson在评论中所提到的,您可以通过更改持续时间(以及速度)来获得更平滑的动画。动画。目前,它的设置速度为1dp / ms

评论


v.measure(MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT,MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT,MeasureSpec.EXACTLY));在某些情况下(我-ListView),这种不匹配会导致错误的targtetHeight值

–Johnny Doe
2013年6月27日18:43



@Tom Esterez这确实有效,但不是很顺利。还有其他工作可以使它顺利进行吗?

–acntwww
13年7月19日在2:12

@acntwww您可以获得平稳的动画,将持续时间乘以某个因子,例如4。a.setDuration(((int)(initialHeight / v.getContext()。getResources()。getDisplayMetrics()。density))* 4)

– Jefferson Henrique C. Soares
2013年9月25日在21:08



@Alioo,导入android.view.animation.Transformation;

–乔米亚
2014年8月25日上午10:42

很棒!我想扩展固定的dp元素时遇到测量高度的问题,因此我将测量值​​更改为v.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED),View.MeasureSpec.makeMeasureSpec(0,View)。 MeasureSpec.UNSPECIFIED));和v.getLayoutParams()。height = interpolatedTime == 1吗? targetHeight:(int)(targetHeight * interpolatedTime);那对我有用!

–vkislicins
15年8月22日在11:27



#2 楼

我今天偶然发现了相同的问题,我想这个问题的真正解决方案是

<LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
 />


您将必须为所有涉及到的最顶层布局设置此属性。在轮班。如果现在将一种布局的可见性设置为GONE,则另一种布局将占用该空间,因为消失的一种布局正在释放它。将有一个默认的动画,某种程度上是“淡出”的,但是我认为您可以更改此动画-但到目前为止,我还没有测试过最后一个动画。

评论


+1,现在我正在搜索Speed:animateLayoutChanges的持续时间

–图莎·潘迪(Tushar Pandey)
2014年4月29日在7:14

动画版式更改:developer.android.com/training/animation/layout.html

–ccpizza
2014年5月18日13:10

按下返回按钮后不起作用。有什么建议?

–哈桑·塔雷克(Hassan Tareq)
17年4月10日在4:58

这对于展开动画非常有效,但对于折叠动画,发生在父布局缩小后进行。

– shine_joseph
'18 Sep 3'在6:43

@shine_joseph是的,我在recyclerview中使用它,当折叠看起来真的很奇怪:/

– AmirG
18-09-28在13:58

#3 楼

我试图做我认为非常相似的动画,并找到了一种优雅的解决方案。此代码假定您始终从0-> h或h-> 0(h为最大高度)出发。构造函数的三个参数是view =要动画化的视图(在我的情况下是webview),targetHeight =视图的最大高度,down =指示方向的布尔值(true =展开,false =折叠)。 br />
public class DropDownAnim extends Animation {
    private final int targetHeight;
    private final View view;
    private final boolean down;

    public DropDownAnim(View view, int targetHeight, boolean down) {
        this.view = view;
        this.targetHeight = targetHeight;
        this.down = down;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        int newHeight;
        if (down) {
            newHeight = (int) (targetHeight * interpolatedTime);
        } else {
            newHeight = (int) (targetHeight * (1 - interpolatedTime));
        }
        view.getLayoutParams().height = newHeight;
        view.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth,
            int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
    }

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


评论


代码中有一个错字:“ initalize”方法名称应为“ initialize”,否则将不会被调用。 ;)我建议以后使用@Override,这样编译器会捕获这种错字。

–Lorne Laliberte
2011-09-28 3:57

我正在执行以下操作:“ DropDownAnim anim = new DropDownAnim(grid_titulos_atual,GRID_HEIGHT,true); anim.setDuration(500); anim.start();”但它不起作用。我在applyTransformation上放置了一些断点,但从未达到

– Paulo Cesar
2011-12-5 14:03

操作,让它正常工作,它是view.startAnimation(a)...性能不是很好,但是它可以工作:)

– Paulo Cesar
2011-12-5 16:16



@IamStalker在这种情况下,您可能应该使用两个变量进行初始化,开始高度和结束高度。然后更改为:if(down){newHeight =(int)((((endingHeight-startingHeight)* interpolatedTime)+ startingHeight); } else {newHeight =(int)((((endingHeight-startingHeight)*(1-interpolatedTime))+ startingHeight); }

–塞斯·尼尔森(Seth Nelson)
2012年6月4日4:44



@Seth我认为newHeight可以简单地是(int)((((targetHeight -startingHeight)* interpolatedTime)+ startingHeight)+起始方向,只要在initialize()中设置了startHeight即可。

– Giorgos Kylafas
2012年9月6日上午8:18

#4 楼

我使用了@LenaYan的解决方案,该解决方案对我来说无法正常运行(因为它在折叠和/或展开之前将View转换为0高度视图)并进行了一些更改。

现在,通过采用View的先前高度并以此尺寸开始扩展,它可以很好地工作。折叠是相同的。

您只需复制并粘贴以下代码即可:

public static void expand(final View v, int duration, int targetHeight) {

    int prevHeight  = v.getHeight();

    v.setVisibility(View.VISIBLE);
    ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}

public static void collapse(final View v, int duration, int targetHeight) {
    int prevHeight  = v.getHeight();
    ValueAnimator valueAnimator = ValueAnimator.ofInt(prevHeight, targetHeight);
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}


用法:

//Expanding the View
   expand(yourView, 2000, 200);

// Collapsing the View     
   collapse(yourView, 2000, 100);


很简单!

感谢LenaYan提供初始代码!

评论


虽然有效,但取决于开发人员设置(动画持续时间)。如果禁用此选项,则不会显示动画。

– CoolMind
16年7月29日在20:48

是的,但这可能是问题,也可能不是问题。取决于您的应用程序。例如,您可以通过简单的更改轻松地使动画的持续时间与扩展/收缩的大小成比例。可设置的动画持续时间为您提供了更多自由。

–杰拉尔多·内图(Geraldo Neto)
16-10-9在1:15



展开动画不起作用。它看起来像崩溃动画。

–阿哈马杜拉(Ahamadullah Saikat)
19/12/28在5:15

#5 楼

一种替代方法是使用具有以下缩放因子的缩放动画进行扩展:

ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);


和折叠:

ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);


评论


如何开始动画。View.startAnimation(anim);似乎不起作用

–马亨德兰
2012年6月28日在8:57



这就是我开始动画的方式。其他动画对您有用吗?

– Christopher
2012年6月28日在9:20

采用这种方法,就像一个魅力,不需要实施已经实施的方法。

–erbsman
2012年10月29日14:32

在动画过程中,这不会将视图下移,并且看起来好像是从0-> h拉伸动画视图一样。

–user901309
2012年12月4日14:47

顺便说一句,视图动画非常适合缩放:oView.animate()。scaleY(0)可以垂直折叠; oView.animate()。scaleY(1)打开(注意,它仅适用于sdk 12及更高版本)。

– Kirk B.
13年1月15日在8:00

#6 楼

@Tom Esterez的答案,但已更新为根据Android getMeasuredHeight正确使用view.measure()返回错误的值!

    // http://easings.net/
    Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

    public static Animation expand(final View view) {
        int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
        int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
        final int targetHeight = view.getMeasuredHeight();

        // Older versions of android (pre API 21) cancel animations for views with a height of 0 so use 1 instead.
        view.getLayoutParams().height = 1;
        view.setVisibility(View.VISIBLE);

        Animation animation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {

               view.getLayoutParams().height = interpolatedTime == 1
                    ? ViewGroup.LayoutParams.WRAP_CONTENT
                    : (int) (targetHeight * interpolatedTime);

            view.requestLayout();
        }

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

        animation.setInterpolator(easeInOutQuart);
        animation.setDuration(computeDurationFromHeight(view));
        view.startAnimation(animation);

        return animation;
    }

    public static Animation collapse(final View view) {
        final int initialHeight = view.getMeasuredHeight();

        Animation a = new Animation() {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if (interpolatedTime == 1) {
                    view.setVisibility(View.GONE);
                } else {
                    view.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
                    view.requestLayout();
                }
            }

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

        a.setInterpolator(easeInOutQuart);

        int durationMillis = computeDurationFromHeight(view);
        a.setDuration(durationMillis);

        view.startAnimation(a);

        return a;
    }

    private static int computeDurationFromHeight(View view) {
        // 1dp/ms * multiplier
        return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density);
    }


评论


什么是addHeight和DURATION_MULTIPLIER?

– MidasLefko
15年10月22日在13:05

忘了这些,addHeight是在扩展中需要额外高度的情况下(可能不需要),而DURATION_MODIFIER只是速度修改器,以用于加速/减速动画。

– Erik B
15-10-22在18:24

很棒!使用TextView的最后一行只有一个单词时,会出现一小段延迟。您能解释一下PathInterpolator的功能吗?

– yennsarah
2015年11月9日在9:17

easeInOutQuart首先使动画变慢,然后再变快,最后使变慢,从而获得非常自然的感觉。他们在这里进一步谈论了easings.net

– Erik B
15年11月9日,19:54

我尝试了您的方法,但是只要动画结束,我的视图就不再可见。

–阿曼·维尔玛(Aman Verma)
16 Dec 21'在15:57

#7 楼

好的,我只是找到了一个非常丑陋的解决方案:

public static Animation expand(final View v, Runnable onEnd) {
    try {
        Method m = v.getClass().getDeclaredMethod("onMeasure", int.class, int.class);
        m.setAccessible(true);
        m.invoke(
            v,
            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
            MeasureSpec.makeMeasureSpec(((View)v.getParent()).getMeasuredHeight(), MeasureSpec.AT_MOST)
        );
    } catch (Exception e){
        Log.e("test", "", e);
    }
    final int initialHeight = v.getMeasuredHeight();
    Log.d("test", "initialHeight="+initialHeight);

    v.getLayoutParams().height = 0;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation()
    {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            final int newHeight = (int)(initialHeight * interpolatedTime);
            v.getLayoutParams().height = newHeight;
            v.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    a.setDuration(5000);
    v.startAnimation(a);
    return a;
}


随意提出更好的解决方案!

评论


+1,即使它被命名为丑陋,它也适用于我们尚不知道其大小的视图(例如,如果我们要向父级添加一个新创建的视图(其大小为FILL_PARENT),并希望为其设置动画这个过程,包括为父级大小的动画制作动画)。

–维特·胡登科(Vit Khudenko)
2012年8月26日13:32

顺便说一句,在View.onMeause(widthMeasureSpec,heightMeasureSpec)调用中似乎有一个小错误,因此应该交换宽度和高度规格。

–维特·胡登科(Vit Khudenko)
2012年8月27日13:24

#8 楼

public static void expand(final View v, int duration, int targetHeight) {
        v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(duration);
        valueAnimator.start();
    }
public static void collapse(final View v, int duration, int targetHeight) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}


评论


我有这个问题...可折叠视图内的内容在扩展时消失了。我有“回收者视图”,该视图在扩展此视图时消失。 @LenaYan

–阿克斯海·马哈詹(Akshay Mahajan)
18年6月10日在11:50

#9 楼

如果您不想一直扩展或折叠-这是一个简单的HeightAnimation-

import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class HeightAnimation extends Animation {
    protected final int originalHeight;
    protected final View view;
    protected float perValue;

    public HeightAnimation(View view, int fromHeight, int toHeight) {
        this.view = view;
        this.originalHeight = fromHeight;
        this.perValue = (toHeight - fromHeight);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        view.getLayoutParams().height = (int) (originalHeight + perValue * interpolatedTime);
        view.requestLayout();
    }

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


用法:

HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());
heightAnim.setDuration(1000);
view.startAnimation(heightAnim);


#10 楼

我改编了汤姆·埃斯特雷兹(Tom Esterez)当前接受的答案,该答案虽然有效,但动画不连贯且不稳定。我的解决方案基本上是将Animation替换为ValueAnimator,它可以与您选择的Interpolator配合使用,以实现各种效果,例如过冲,反弹,加速等。

此解决方案非常适合具有动态高度(即使用WRAP_CONTENT),因为它首先测量实际所需的高度,然后动画到该高度。

public static void expand(final View v) {
    v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    final int targetHeight = v.getMeasuredHeight();

    // Older versions of android (pre API 21) cancel animations for views with a height of 0.
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);

    ValueAnimator va = ValueAnimator.ofInt(1, targetHeight);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    va.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            v.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
        }

        @Override public void onAnimationStart(Animator animation) {}
        @Override public void onAnimationCancel(Animator animation) {}
        @Override public void onAnimationRepeat(Animator animation) {}
    });
    va.setDuration(300);
    va.setInterpolator(new OvershootInterpolator());
    va.start();
}

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    ValueAnimator va = ValueAnimator.ofInt(initialHeight, 0);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (Integer) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    va.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationEnd(Animator animation) {
            v.setVisibility(View.GONE);
        }

        @Override public void onAnimationStart(Animator animation) {}
        @Override public void onAnimationCancel(Animator animation) {}
        @Override public void onAnimationRepeat(Animator animation) {}
    });
    va.setDuration(300);
    va.setInterpolator(new DecelerateInterpolator());
    va.start();
}


然后您只需致电expand( myView );collapse( myView );

评论


谢谢。您还可以添加最小高度不为0的情况。

– CoolMind
19年4月30日在14:50

刚刚更正了v.measure()中使用的参数,现在它可以正常工作。谢谢!

– Shahood ul Hassan
5月9日6:20

展开时振动视图

–阿布·纳耶姆(Abu Nayem)
10月6日下午16:25

#11 楼

利用Kotlin扩展功能,该功能已经过测试,答案最短

只需在任何视图上调用animateVisibility(展开/折叠)即可。

fun View.animateVisibility(setVisible: Boolean) {
    if (setVisible) expand(this) else collapse(this)
}

private fun expand(view: View) {
    view.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
    val initialHeight = 0
    val targetHeight = view.measuredHeight

    // Older versions of Android (pre API 21) cancel animations for views with a height of 0.
    //v.getLayoutParams().height = 1;
    view.layoutParams.height = 0
    view.visibility = View.VISIBLE

    animateView(view, initialHeight, targetHeight)
}

private fun collapse(view: View) {
    val initialHeight = view.measuredHeight
    val targetHeight = 0

    animateView(view, initialHeight, targetHeight)
}

private fun animateView(v: View, initialHeight: Int, targetHeight: Int) {
    val valueAnimator = ValueAnimator.ofInt(initialHeight, targetHeight)
    valueAnimator.addUpdateListener { animation ->
        v.layoutParams.height = animation.animatedValue as Int
        v.requestLayout()
    }
    valueAnimator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationEnd(animation: Animator) {
            v.layoutParams.height = targetHeight
        }

        override fun onAnimationStart(animation: Animator) {}
        override fun onAnimationCancel(animation: Animator) {}
        override fun onAnimationRepeat(animation: Animator) {}
    })
    valueAnimator.duration = 300
    valueAnimator.interpolator = DecelerateInterpolator()
    valueAnimator.start()
}


评论


想要发布相同的答案:)太可惜了,这里太深了。

–muetzenflo
19年5月30日在10:44

@muetzenflo如果越来越多的人支持答案,它将出现。 :)

–拉吉兰
19年5月30日在11:26

我喜欢这种解决方案,直到我意识到如果有一个textview包含多行高度为wrap_content的行,展开时,textview将只显示一行。我正在尝试修复

–詹姆斯
19-10-18在13:08



我试过了,但是动画看起来不流畅。为了进行扩展,整个文本视图会短暂出现一次,然后播放动画。对于折叠,由于某种原因,textview在折叠后立即立即再次展开。知道我在做什么错吗?

–安吉思·阿查里亚(Anchith Acharya)
6月11日19:05

#12 楼

除了汤姆·埃斯特雷斯(Tom Esterez)的出色回答和埃里克·B(Erik B)的出色更新之外,我还想发表自己的看法,将扩展和收缩方法合并为一个。这样,您可以例如执行以下操作...

button.setOnClickListener(v -> expandCollapse(view));

onClick()...

public static void expandCollapse(View view) {

    boolean expand = view.getVisibility() == View.GONE;
    Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

    view.measure(
        View.MeasureSpec.makeMeasureSpec(((View) view.getParent()).getWidth(), View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
    );

    int height = view.getMeasuredHeight();
    int duration = (int) (height/view.getContext().getResources().getDisplayMetrics().density);

    Animation animation = new Animation() {
        @Override protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (expand) {
                view.getLayoutParams().height = 1;
                view.setVisibility(View.VISIBLE);
                if (interpolatedTime == 1) {
                    view.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
                } else {
                    view.getLayoutParams().height = (int) (height * interpolatedTime);
                }
                view.requestLayout();
            } else {
                if (interpolatedTime == 1) {
                    view.setVisibility(View.GONE);
                } else {
                    view.getLayoutParams().height = height - (int) (height * interpolatedTime);
                    view.requestLayout();
                }
            }
        }
        @Override public boolean willChangeBounds() {
            return true;
        }
    };

    animation.setInterpolator(easeInOutQuart);
    animation.setDuration(duration);
    view.startAnimation(animation);

}


评论


我尝试了此代码,但要使其在多个视图上工作,则必须滚动。知道我该如何解决吗? stackoverflow.com/q/43916369/1009507

–sammyukavi
17年5月11日在13:17

@Ukavi我在多个视图中使用它,并且在ScrollView中可以正常工作。

–mjp66
17年5月12日在7:16

在recyclerview中呢?

–sammyukavi
17年5月12日在14:20

@Ukavi还不需要在recyclerview中使用它,但是我不明白为什么它不起作用。您必须自己尝试一下;)

–mjp66
17年5月13日在9:50



#13 楼

我想在上面的非常有用的答案中添加一些内容。如果您不知道高度,因为视图.getHeight()返回0,则可以执行以下操作来获取高度:

contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION);
int finalHeight = view.getMeasuredHeight();


其中DUMMY_HIGH_DIMENSIONS是您的视图的宽度/高度(以像素为单位),当使用ScrollView封装视图时,拥有一个很大的数字是合理的。

#14 楼

这是我用来通过动画调整视图(LinearLayout)宽度的代码段。

代码应该根据目标大小进行扩展或收缩。如果需要fill_parent宽度,则必须将父.getMeasuredWidth设置为目标宽度,同时将标志设置为true。

希望对您有所帮助。

}

评论


有什么技巧可以使它正常工作吗?该类将获得正确的原始宽度和目标宽度,但是我的视图不会调整大小。我正在使用resizeAnim.start()。也尝试过有和没有setFillAfter(true)

–本·凯恩(Ben Kane)
13年8月19日在21:58

得到它了。必须在视图上调用.startAnimation(resizeAnim)。

–本·凯恩(Ben Kane)
13年8月19日在22:02

#15 楼

对于平滑动画,请使用带有run方法的Handler。....并享受Expand / Collapse animation

    class AnimUtils{

                 public void expand(final View v) {
                  int ANIMATION_DURATION=500;//in milisecond
        v.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        final int targtetHeight = v.getMeasuredHeight();

        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        Animation a = new Animation()
        {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                v.getLayoutParams().height = interpolatedTime == 1
                        ? LayoutParams.WRAP_CONTENT
                        : (int)(targtetHeight * interpolatedTime);
                v.requestLayout();
            }

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

        // 1dp/ms
        a.setDuration(ANIMATION_DURATION);

      // a.setDuration((int)(targtetHeight / v.getContext().getResources().getDisplayMetrics().density));
        v.startAnimation(a);
    }



    public void collapse(final View v) {
        final int initialHeight = v.getMeasuredHeight();

        Animation a = new Animation()
        {
            @Override
            protected void applyTransformation(float interpolatedTime, Transformation t) {
                if(interpolatedTime == 1){
                    v.setVisibility(View.GONE);
                }else{
                    v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                    v.requestLayout();
                }
            }

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

        // 1dp/ms
        a.setDuration(ANIMATION_DURATION);
       // a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
        v.startAnimation(a);
    }
}


并使用以下代码调用:

       private void setAnimationOnView(final View inactive ) {
    //I am applying expand and collapse on this TextView ...You can use your view 

    //for expand animation
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

            new AnimationUtililty().expand(inactive);

        }
    }, 1000);


    //For collapse
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {

            new AnimationUtililty().collapse(inactive);
            //inactive.setVisibility(View.GONE);

        }
    }, 8000);

}


其他解决方案是:

               public void expandOrCollapse(final View v,String exp_or_colpse) {
    TranslateAnimation anim = null;
    if(exp_or_colpse.equals("expand"))
    {
        anim = new TranslateAnimation(0.0f, 0.0f, -v.getHeight(), 0.0f);
        v.setVisibility(View.VISIBLE);  
    }
    else{
        anim = new TranslateAnimation(0.0f, 0.0f, 0.0f, -v.getHeight());
        AnimationListener collapselistener= new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
            v.setVisibility(View.GONE);
            }
        };

        anim.setAnimationListener(collapselistener);
    }

     // To Collapse
        //

    anim.setDuration(300);
    anim.setInterpolator(new AccelerateInterpolator(0.5f));
    v.startAnimation(anim);
}


#16 楼

是的,我同意以上评论。实际上,似乎正确(或至少最简单的方法)是(在XML中)指定初始布局高度“ 0px”-然后您可以为“ toHeight”传递另一个参数(您的自定义动画子类的构造函数的“最终高度”),例如在上面的示例中,它看起来像这样:

    public DropDownAnim( View v, int toHeight ) { ... }


无论如何,希望对您有所帮助! :)

#17 楼

这是我的解决方案。我认为这更简单。它只会扩展视图,但可以轻松扩展。

public class WidthExpandAnimation extends Animation
{
    int _targetWidth;
    View _view;

    public WidthExpandAnimation(View view)
    {
        _view = view;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        if (interpolatedTime < 1.f)
        {
            int newWidth = (int) (_targetWidth * interpolatedTime);

            _view.layout(_view.getLeft(), _view.getTop(),
                    _view.getLeft() + newWidth, _view.getBottom());
        }
        else
            _view.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight)
    {
        super.initialize(width, height, parentWidth, parentHeight);

        _targetWidth = width;
    }

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


#18 楼

我认为最简单的解决方案是将android:animateLayoutChanges="true"设置为您的LinearLayout,然后通过设置其可见性仅显示/隐藏视图。像吊饰一样工作,但您无法控制动画的持续时间

#19 楼

@Tom Esterez和@Geraldo Neto的组合解决方案

public static void expandOrCollapseView(View v,boolean expand){

    if(expand){
        v.measure(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        final int targetHeight = v.getMeasuredHeight();
        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        ValueAnimator valueAnimator = ValueAnimator.ofInt(targetHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(500);
        valueAnimator.start();
    }
    else
    {
        final int initialHeight = v.getMeasuredHeight();
        ValueAnimator valueAnimator = ValueAnimator.ofInt(initialHeight,0);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
                if((int)animation.getAnimatedValue() == 0)
                    v.setVisibility(View.GONE);
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(500);
        valueAnimator.start();
    }
}

//sample usage
expandOrCollapseView((Your ViewGroup),(Your ViewGroup).getVisibility()!=View.VISIBLE);


#20 楼

您走在正确的轨道上。确保在动画开始之前将v1的布局高度设置为零。您希望在开始动画之前将设置初始化为看起来像动画的第一帧。

评论


我同意,但是如果这样做,如何获取initialHeight(动画所需)?

–汤姆·埃斯特雷斯(Tom Esterez)
2011年2月9日15:39

您是否尝试过实际上只是将初始高度保存在Initialize中,将视图设置为可见,然后设置v.getLayoutParams()。height = 0 ;?直接之后,全部在初始化吗?

– Micah Hainline
2011年2月9日在20:12

是的,如果我这样做,则初始化方法的高度为0

–汤姆·埃斯特雷斯(Tom Esterez)
2011年2月9日在21:02

#21 楼

这是我的解决方法,我的ImageView100%增长到200%,并使用res/anim/文件夹中的两个动画文件恢复到其原始大小。

anim_grow.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="1.0"
  android:toXScale="2.0"
  android:fromYScale="1.0"
  android:toYScale="2.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>


anim_shrink.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="2.0"
  android:toXScale="1.0"
  android:fromYScale="2.0"
  android:toYScale="1.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>


ImageView发送到我的方法setAnimationGrowShrink()

ImageView img1 = (ImageView)findViewById(R.id.image1);
setAnimationGrowShrink(img1);


setAnimationGrowShrink()方法:

private void setAnimationGrowShrink(final ImageView imgV){
    final Animation animationEnlarge = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_grow);
    final Animation animationShrink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_shrink);

    imgV.startAnimation(animationEnlarge);

    animationEnlarge.setAnimationListener(new AnimationListener() {         
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationShrink);
        }
    });

    animationShrink.setAnimationListener(new AnimationListener() {          
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationEnlarge);
        }
    });

}


#22 楼

这是一个正确的工作解决方案,我已经对其进行了测试:

扩展名:

private void expand(View v) {
    v.setVisibility(View.VISIBLE);

    v.measure(View.MeasureSpec.makeMeasureSpec(PARENT_VIEW.getWidth(), View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

    final int targetHeight = v.getMeasuredHeight();

    mAnimator = slideAnimator(0, targetHeight);
    mAnimator.setDuration(800);
    mAnimator.start();
}


崩溃:

private void collapse(View v) {
    int finalHeight = v.getHeight();

    mAnimator = slideAnimator(finalHeight, 0);

    mAnimator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {

        }

        @Override
        public void onAnimationEnd(Animator animator) {
            //Height=0, but it set visibility to GONE
            llDescp.setVisibility(View.GONE);
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    mAnimator.start();
}


值动画师:

private ValueAnimator slideAnimator(int start, int end) {
    ValueAnimator mAnimator = ValueAnimator.ofInt(start, end);

    mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            //Update Height
            int value = (Integer) valueAnimator.getAnimatedValue();
            ViewGroup.LayoutParams layoutParams = llDescp.getLayoutParams();
            layoutParams.height = value;
            v.setLayoutParams(layoutParams);
        }
    });
    return mAnimator;
}


视图v是要进行动画处理的视图,PARENT_VIEW是包含该视图的容器视图。

#23 楼

使用droidQuery确实很简单。首先,请考虑以下布局:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    <LinearLayout
        android:id="@+id/v1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 1" />
    </LinearLayout>
    <LinearLayout
        android:id="@+id/v2"
        android:layout_width="wrap_content"
        android:layout_height="0dp" >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 2" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:text="View 3" />
    </LinearLayout>
</LinearLayout>


我们可以使用以下代码将高度动画化为所需的值-例如100dp-:

//convert 100dp to pixel value
int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());


然后使用droidQuery进行动画处理。最简单的方法是:

$.animate("{ height: " + height + "}", new AnimationOptions());


要使动画更具吸引力,请考虑添加缓动:

$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE));


您还可以使用AnimationOptions方法更改duration()上的持续时间,或处理动画结束时发生的情况。对于复杂的示例,请尝试:

$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE)
                                                             .duration(1000)
                                                             .complete(new Function() {
                                                                 @Override
                                                                 public void invoke($ d, Object... args) {
                                                                     $.toast(context, "finished", Toast.LENGTH_SHORT);
                                                                 }
                                                             }));


#24 楼

展开/折叠视图的最佳解决方案:

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        View view = buttonView.getId() == R.id.tb_search ? fSearch : layoutSettings;
        transform(view, 200, isChecked
            ? ViewGroup.LayoutParams.WRAP_CONTENT
            : 0);
    }

    public static void transform(final View v, int duration, int targetHeight) {
        int prevHeight  = v.getHeight();
        v.setVisibility(View.VISIBLE);
        ValueAnimator animator;
        if (targetHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
            v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            animator = ValueAnimator.ofInt(prevHeight, v.getMeasuredHeight());
        } else {
            animator = ValueAnimator.ofInt(prevHeight, targetHeight);
        }
        animator.addUpdateListener(animation -> {
            v.getLayoutParams().height = (animation.getAnimatedFraction() == 1.0f)
                    ? targetHeight
                    : (int) animation.getAnimatedValue();
            v.requestLayout();
        });
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(duration);
        animator.start();
    }


评论


尽管有效,但它还取决于开发人员设置(动画持续时间)。并完善您的代码,删除lambda函数并重新格式化onCheckedChanged。

– CoolMind
16年7月29日在20:47

为什么在更改v的LayoutParams之后仅在v上调用requestLayout就足够了?我认为有必要在v的父级上调用requestLayout

–vlazzle
17年8月25日在7:43

#25 楼

您可以稍微扭转一下使用ViewPropertyAnimator。要折叠,请将视图缩放到1个像素的高度,然后将其隐藏。要展开,请显示它,然后将其展开到其高度。

private void collapse(final View view) {
    view.setPivotY(0);
    view.animate().scaleY(1/view.getHeight()).setDuration(1000).withEndAction(new Runnable() {
        @Override public void run() {
            view.setVisibility(GONE);
        }
    });
}

private void expand(View view, int height) {
    float scaleFactor = height / view.getHeight();

    view.setVisibility(VISIBLE);
    view.setPivotY(0);
    view.animate().scaleY(scaleFactor).setDuration(1000);
}


枢轴告诉视图缩放的位置,默认位置在中间。持续时间是可选的(默认= 1000)。您还可以设置要使用的插值器,例如.setInterpolator(new AccelerateDecelerateInterpolator())

#26 楼

基于@Tom Esterez和@Seth Nelson(排名前2)的解决方案,我对其进行了简化。与原始解决方案一样,它也不依赖于开发人员选项(动画设置)。

private void resizeWithAnimation(final View view, int duration, final int targetHeight) {
    final int initialHeight = view.getMeasuredHeight();
    final int distance = targetHeight - initialHeight;

    view.setVisibility(View.VISIBLE);

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if (interpolatedTime == 1 && targetHeight == 0) {
                view.setVisibility(View.GONE);
            }
            view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
            view.requestLayout();
        }

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

    a.setDuration(duration);
    view.startAnimation(a);
}


评论


好了,三年后,我再次测试了几种解决方案,但只有我的解决方案正确。

– CoolMind
19年4月30日在15:25

#27 楼

我创建了不需要指定布局高度的版本,因此它使用起来更容易,更清洁。解决方案是在动画的第一帧中获取高度(当时至少在我的测试中可用)。这样,您就可以为View提供任意高度和底边距。

构造函数中还有一个小技巧-底边距设置为-10000,以便在转换前视图保持隐藏状态(防止闪烁)。

public class ExpandAnimation extends Animation {


    private View mAnimatedView;
    private ViewGroup.MarginLayoutParams mViewLayoutParams;
    private int mMarginStart, mMarginEnd;

    public ExpandAnimation(View view) {
        mAnimatedView = view;
        mViewLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        mMarginEnd = mViewLayoutParams.bottomMargin;
        mMarginStart = -10000; //hide before viewing by settings very high negative bottom margin (hack, but works nicely)
        mViewLayoutParams.bottomMargin = mMarginStart;
        mAnimatedView.setLayoutParams(mViewLayoutParams);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
            //view height is already known when the animation starts
            if(interpolatedTime==0){
                mMarginStart = -mAnimatedView.getHeight();
            }
            mViewLayoutParams.bottomMargin = (int)((mMarginEnd-mMarginStart) * interpolatedTime)+mMarginStart;
            mAnimatedView.setLayoutParams(mViewLayoutParams);
    }
}


#28 楼

使用ValueAnimator:

ValueAnimator expandAnimation = ValueAnimator.ofInt(mainView.getHeight(), 400);
expandAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(final ValueAnimator animation) {
        int height = (Integer) animation.getAnimatedValue();
        RelativeLayout.LayoutParams lp = (LayoutParams) mainView.getLayoutParams();
        lp.height = height;
    }
});


expandAnimation.setDuration(500);
expandAnimation.start();


评论


就我而言,什么都不做。您也可以简化代码,将2行折叠成mainView.getLayoutParams()。height = height。

– CoolMind
16年7月29日在20:34

#29 楼

public static void slide(View v, int speed, int pos) {
    v.animate().setDuration(speed);
    v.animate().translationY(pos);
    v.animate().start();
}

// slide down
slide(yourView, 250, yourViewHeight);
// slide up
slide(yourView, 250, 0);


#30 楼

/**
 * Animation that either expands or collapses a view by sliding it down to make
 * it visible. Or by sliding it up so it will hide. It will look like it slides
 * behind the view above.
 * 
 */
public class FinalExpandCollapseAnimation extends Animation
{
    private View mAnimatedView;
    private int mEndHeight;
    private int mType;
    public final static int COLLAPSE = 1;
    public final static int EXPAND = 0;
    private LinearLayout.LayoutParams mLayoutParams;
    private RelativeLayout.LayoutParams mLayoutParamsRel;
    private String layout;
    private Context context;

    /**
     * Initializes expand collapse animation, has two types, collapse (1) and
     * expand (0).
     * 
     * @param view
     *            The view to animate
     * @param type
     *            The type of animation: 0 will expand from gone and 0 size to
     *            visible and layout size defined in xml. 1 will collapse view
     *            and set to gone
     */
    public FinalExpandCollapseAnimation(View view, int type, int height, String layout, Context context)
    {
        this.layout = layout;
        this.context = context;
        mAnimatedView = view;
        mEndHeight = mAnimatedView.getMeasuredHeight();
        if (layout.equalsIgnoreCase("linear"))
            mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
        else
            mLayoutParamsRel = ((RelativeLayout.LayoutParams) view.getLayoutParams());
        mType = type;
        if (mType == EXPAND)
        {
            AppConstant.ANIMATED_VIEW_HEIGHT = height;
        }
        else
        {
            if (layout.equalsIgnoreCase("linear"))
                mLayoutParams.topMargin = 0;
            else
                mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
        }
        setDuration(600);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        super.applyTransformation(interpolatedTime, t);
        if (interpolatedTime < 1.0f)
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                mAnimatedView.setVisibility(View.VISIBLE);
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
                else
                    mLayoutParamsRel.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
            }
            mAnimatedView.requestLayout();
        }
        else
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParams.topMargin = 0;
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
                }
                mAnimatedView.setVisibility(View.VISIBLE);
                mAnimatedView.requestLayout();
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = 0;
                else
                    mLayoutParamsRel.height = 0;
                mAnimatedView.setVisibility(View.GONE);
                mAnimatedView.requestLayout();
            }
        }
    }

    private int convertPixelsIntoDensityPixels(int pixels)
    {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        return (int) metrics.density * pixels;
    }
}


可以按以下方式调用该类

   if (findViewById(R.id.ll_specailoffer_show_hide).getVisibility() == View.VISIBLE) {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown_up);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.COLLAPSE,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    } else {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.EXPAND,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    }