Android: развернуть / свернуть анимацию
допустим, у меня есть вертикальный linearLayout с :
[v1]
[v2]
по умолчанию v1 visibily = ушел. Я хотел бы показать 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?
30 ответов:
Я вижу, что этот вопрос стал популярным, так что я отправляю мое решение. Главное преимущество заключается в том, что вам не нужно знать развернутую высоту, чтобы применить анимацию, и как только представление расширяется, оно адаптирует высоту при изменении содержимого. Он отлично работает для меня.
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 (ч максимальная высота). Три параметра конструктора-view = вид, который должен быть анимирован (в моем случае, webview), targetHeight = максимальная высота представления и down = логическое значение, которое указывает направление (true = расширение, false = сворачивание).
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's решение это не сработало должным образом ко мне (потому что он преобразовывал вид в вид 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 за исходный код!
альтернативой является использование масштабной анимации со следующими коэффициентами масштабирования для расширения:
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; }
Не стесняйтесь предложить лучшее решение !
@Тома Esterez это ответ, но обновлен для использования view.измерьте () правильно в 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.
Это фрагмент, который я использовал для изменения ширины представления (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; }
}
для плавной анимации пожалуйста, используйте обработчик с методом Run.....И наслаждайтесь развернуть / свернуть анимацию
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); }
добавление к тому Эстерез отличный ответ и Эрик Б отличное обновление к нему я думал, что отправлю свой собственный дубль, сжимая методы расширения и контракта в один. Таким образом, вы могли бы, например, иметь такое действие...
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); }
Я думаю, что самое простое решение-установить
android:animateLayoutChanges="true"
наLinearLayout
а затем просто показать / скрыть вид, установив его видимость. Работает как шарм, но у вас нет контроля над продолжительностью анимации
Да, я согласна с комментариями выше. И действительно, это кажется правильным (или, по крайней мере, самым легким?) нужно указать (в XML) начальную высоту макета " 0px "- и тогда вы можете передать другой аргумент для " toHeight "(т. е." конечная высота") конструктору вашего пользовательского подкласса анимации, например, в приведенном выше примере это будет выглядеть примерно так:
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; } }
комбинированные решения от @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);
вы на правильном пути. Убедитесь, что у вас установлен v1 с нулевой высотой макета прямо перед началом анимации. Перед запуском анимации необходимо инициализировать настройку, чтобы она выглядела как первый кадр анимации.
убедитесь, что у вас есть v1 установлен, чтобы иметь высоту макета нуля прямо перед началом анимации. Перед запуском анимации необходимо инициализировать настройку, чтобы она выглядела как первый кадр анимации.
это было мое решение, мой
ImageView
растет100%
до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); } }); }
это очень просто с 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); } }));
лучшее решение для развернуть/свернуть вида:
@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(); }
Вы можете использовать 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())
Это правильное рабочее решение, я проверил это:
Exapnd:
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; }
посмотреть в вид, чтобы быть анимированные, PARENT_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); } }
Использовать 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();
на основе решений @Tom Esterez и @Seth Nelson (top 2) я симулировал их. Как и оригинальные решения, он не зависит от параметров разработчика (настройки анимации).
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; } }
класс может быть вызван следующим образом
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(); }
я адаптировал принятый в настоящее время ответ Тома Эстереза, который работал, но имел прерывистую и не очень гладкую анимацию. Мое решение в основном заменяет
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 );
.