Android:展开/折叠animation
比方说,我有一个垂直线性布局:
[v1] [v2]
默认v1有visibily = GONE。 我想用展开的animation展示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; } };
但是有了这个解决scheme,animation开始的时候我就眨眼了。 我认为这是由于v1在animation应用之前显示全尺寸造成的。
用javascript,这是jQuery的一行! 任何简单的方法来做到这一点与Android?
我看到这个问题变得很stream行,所以我张贴我的实际解决scheme。 主要优点是您不必知道展开的高度来应用animation,一旦展开视图,就会在内容更改时调整高度。 这对我很好。
public static void expand(final View v) { v.measure(LayoutParams.MATCH_PARENT, 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); 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; } }; // 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; } }; // 1dp/ms a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)); v.startAnimation(a); }
我试图做我认为是非常相似的animation,并find了一个优雅的解决scheme。 这个代码假定你总是从0-> h或h-> 0(h是最大高度)。 三个构造函数参数是视图=要animation的视图(在我的情况下,一个Web视图),targetHeight =视图的最大高度,down =指定方向(true = expanded,false = collapsing)的布尔值。
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; } }
我今天偶然发现了同样的问题,我想这个问题的真正的解决scheme是这样的
<LinearLayout android:id="@+id/container" android:animateLayoutChanges="true" ... />
你将不得不为这个属性设置所有最顶层的布局,这些布局都涉及到这个转换。 如果你现在把一个布局的可见性设置为GONE,另一个布局将消失的空间释放它。 会有一个默认的animation是某种“淡出”,但我认为你可以改变这个 – 但是现在我还没有testing过最后一个。
我拿@LenaYan的解决scheme ,对我来说工作不正常( 因为它正在将视图转换为0高度视图,然后进行折叠和/或展开 )并做了一些更改。
现在,它的工作很好 ,通过采取视图的以前的 高度,并开始扩大与这个大小。 折叠是一样的。
您可以简单地复制并粘贴以下代码:
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的初始代码!
另一种方法是使用具有以下缩放因子的缩放animation进行展开:
ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);
和崩溃:
ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
好吧,我刚刚发现了一个非常丑陋的解决scheme:
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; }
随意提出一个更好的解决scheme!
@Tom Esterez的答案 ,但更新使用view.measure()适当每个Android getMeasuredHeight返回错误的值!
// 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); }
如果你不想展开或者一路崩溃 – 这里是一个简单的高度animation –
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);
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(); }
我想添加一些上面非常有用的答案 。 如果你不知道你的视图.getHeight()返回0的高度,你可以通过下面的命令来获得高度:
contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION); int finalHeight = view.getMeasuredHeight();
DUMMY_HIGH_DIMENSIONS DUMMY_HIGH_DIMENSIONS是宽度/高度(以像素为单位),您的视图被限制为…有了这个巨大的数字是合理的,当视图封装与ScrollView。
这是我用animation调整视图宽度(LinearLayout)的片段。
代码应该根据目标大小进行扩展或收缩。 如果你想要一个fill_parent的宽度,你必须传递父目标.getMeasuredWidth作为目标宽度,同时设置标志为true。
希望它能帮助你们中的一些人。
public class WidthResizeAnimation extends Animation { int targetWidth; int originaltWidth; View view; boolean expand; int newWidth = 0; boolean fillParent; public WidthResizeAnimation(View view, int targetWidth, boolean fillParent) { this.view = view; this.originaltWidth = this.view.getMeasuredWidth(); this.targetWidth = targetWidth; newWidth = originaltWidth; if (originaltWidth > targetWidth) { expand = false; } else { expand = true; } this.fillParent = fillParent; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (expand && newWidth < targetWidth) { newWidth = (int) (newWidth + (targetWidth - newWidth) * interpolatedTime); } if (!expand && newWidth > targetWidth) { newWidth = (int) (newWidth - (newWidth - targetWidth) * interpolatedTime); } if (fillParent && interpolatedTime == 1.0) { view.getLayoutParams().width = -1; } else { view.getLayoutParams().width = newWidth; } 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; }
}
对于stream畅的animation,请使用处理程序与运行方法…..和享受展开/收起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); }
其他解决scheme是:
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); }
我认为最简单的解决scheme是将android:animateLayoutChanges="true"
为您的LinearLayout
,然后通过设置其可见性来显示/隐藏视图。 像魅力一样工作,但是你无法控制animation持续时间
除了Tom Esterez的出色答案和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); }
是的,我同意上述意见。 事实上,它看起来像是正确的(或者至less是最简单的)要做的就是指定(在XML中)初始布局高度“0px” – 然后你可以传入另一个参数“toHeight”(即“最终高度”)到您的自定义animation子类的构造函数,例如在上面的例子中,它看起来像这样:
public DropDownAnim( View v, int toHeight ) { ... }
无论如何,希望有所帮助! 🙂
这是我的解决scheme。 我觉得比较简单。 它只扩展视图,但可以很容易地扩展。
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; } }
你在正确的轨道上。 确保在animation开始之前将v1设置为具有零布局高度。 在开始animation之前,您想要将您的设置初始化为animation的第一帧。
确保在animation开始之前将v1设置为具有零布局高度。 在开始animation之前,您想要将您的设置初始化为animation的第一帧。
这是我的解决scheme,我的ImageView
从100%
增长到200%
并恢复到他原来的大小,使用res/anim/
文件夹内的两个animation文件
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); } }); }
这对于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
来animation。 最简单的方法是这样的:
$.animate("{ height: " + height + "}", new AnimationOptions());
为了使animation更具吸引力,可以考虑添加一个缓动:
$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE));
您也可以使用duration()
方法更改AnimationOptions
的duration()
,或者处理animation结束时发生的情况。 对于一个复杂的例子,请尝试:
$.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); } }));
你可以稍微使用一个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())
我创build的版本中不需要指定布局高度,因此使用起来更容易和更清晰。 解决方法是在animation的第一帧中获取高度(至less在我的testing中,此时可用)。 这样,您可以提供任意高度和底部边距的视图。
在构造函数中还有一点小技巧 – 底部边距设置为-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); } }
Use 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();
Best solution for expand/collapse view's:
@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(); }
Based on solutions by @Tom Esterez and @Seth Nelson (top 2) I simlified them. As well as original solutions it doesn't depend on Developer options (animation settings).
private void resizeWithAnimation(final View view, int duration, final int targetHeight) { final int initialHeight = view.getMeasuredHeight(); final int distance = targetHeight - initialHeight; Animation a = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (interpolatedTime == 1 && targetHeight == 0) { view.setVisibility(View.GONE); } else { view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime); view.requestLayout(); } } @Override public boolean willChangeBounds() { return true; } }; a.setDuration(duration); view.startAnimation(a); }
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);
/** * 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; } }
The class can be called in following way
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(); }
combined solutions from @Tom Esterez and @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);