Android: развернуть / свернуть анимацию

Предположим, у меня есть вертикальная линейная линейка с:

[v1] [v2] 

По умолчанию v1 имеет visibily = 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?

Я вижу, что этот вопрос стал популярным, поэтому я публикую свое фактическое решение. Главное преимущество заключается в том, что вам не нужно знать расширенную высоту, чтобы применить анимацию, и как только расширение будет расширено, оно адаптирует высоту, если содержимое изменится. Это отлично работает для меня.

 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); } 

Я пытался сделать то, что, по моему мнению, было очень похожей анимацией и нашел элегантное решение. Этот код предполагает, что вы всегда переходите от 0-> h или h-> 0 (h – максимальная высота). Три параметра конструктора: view = анимированное представление (в моем случае, webview), targetHeight = максимальная высота представления и down = a boolean, который определяет направление (true = expand, 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; } } 

Сегодня я наткнулся на ту же проблему, и я думаю, что реальное решение этого вопроса

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

Вам нужно будет установить это свойство для всех верхних макетов, которые задействованы в смене. Если теперь вы установите видимость одного макета в GONE, другой будет занимать пространство, поскольку исчезающий его освобождает. Будет анимация по умолчанию, которая является своего рода «угасанием», но я думаю, вы можете изменить это, но последнее, которое я еще не тестировал, пока.

Я принял решение @LenaYan, которое не сработало должным образом ( потому что он преобразовывал представление «Вид в верхний», а затем свертывался и / или расширялся ) и внес некоторые изменения.

Теперь он отлично работает , принимая предыдущую высоту представления и начинаю расширяться с этим размером. Свертывание – то же самое.

Вы можете просто скопировать и вставить код ниже:

 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 за исходный код!

Альтернативой является использование анимации масштаба со следующими коэффициентами масштабирования для расширения:

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

И для свертывания:

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

Хорошо, я просто нашел ОЧЕНЬ уродливое решение:

 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; } 

Не стесняйтесь предлагать лучшее решение!

Ответ @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); } 

Если вы не хотите расширять или рушиться полностью – вот простая версия 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); 
 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 – это ширина / высота (в пикселях), ваше представление ограничено … имея это огромное количество, разумно, когда представление инкапсулируется с помощью ScrollView.

Для гладкой анимации используйте Handler с методом run … И наслаждайтесь анимацией Expand / Collapse

  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); } 

Это фрагмент, который я использовал для изменения ширины представления (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; } 

}

Я думаю, что самым простым решением является установка android:animateLayoutChanges="true" для вашего LinearLayout а затем просто показать / скрыть представление, установив его видимость. Работает как шарм, но у вас нет контроля над продолжительностью анимации

Да, я согласился с вышеуказанными комментариями. И действительно, похоже, что правильная (или, по крайней мере, самая простая?) Вещь – указать (в XML) начальную высоту макета «0px» – и затем вы можете передать другой аргумент для «toHeight» ( Т.е. «конечная высота») для конструктора вашего пользовательского подкласса Animation, например, в приведенном выше примере, он выглядит примерно так:

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

В любом случае, надеюсь, что это поможет! 🙂

Вот мое решение. Я думаю, что это проще. Он только расширяет представление, но его легко продлить.

 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; } } 

Ты на правильном пути. Убедитесь, что вы установили v1, чтобы иметь высоту макета нуля до начала анимации. Перед началом анимации вы хотите инициализировать свою настройку, чтобы выглядеть как первый кадр анимации.

Убедитесь, что вы установили v1, чтобы иметь высоту макета нуля до начала анимации. Перед началом анимации вы хотите инициализировать свою настройку, чтобы выглядеть как первый кадр анимации.

Это мое решение, мой ImageView растет от 100% до 200% и возвращается к его первоначальному размеру, используя два файла анимации внутри res/anim/ folder

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); } }); } 

Добавив к превосходному ответу Тома Эстереза ​​и отличному обновлению Эрика Б, я подумал, что я опубликую свой собственный подход, уплотняя методы расширения и контракта в одном. Таким образом, вы можете, например, выполнить такое действие …

 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); } 

Это действительно просто с 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); } })); 

Вы можете использовать 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())

Я создал версию, в которой вам не нужно указывать высоту макета, поэтому ее намного проще и чище использовать. Решение состоит в том, чтобы получить высоту в первом кадре анимации (она доступна в этот момент, по крайней мере во время моих тестов). Таким образом, вы можете предоставить представление с произвольной высотой и нижним краем.

В конструкторе также есть один маленький хак – нижний край установлен на -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(); }