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


449

Допустим, у меня есть вертикальный linearLayout с:

[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?

Ответы:


734

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

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

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

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

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

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

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

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

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

Как упомянуто @Jefferson в комментариях, вы можете получить более плавную анимацию, изменив продолжительность (и, следовательно, скорость) анимации. В настоящее время он установлен на скорости 1 дп / мс


13
v.measure (MeasureSpec.makeMeasureSpec (LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec (LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); В некоторых случаях (мой - ListView) это несоответствие приводит к неправильному значению targtetHeight
— Джонни Доу

12
@Tom Esterez Это работает, но не очень гладко. Есть ли дополнительная работа, чтобы сделать это гладко?
— acntwww

9
@acntwww Вы можете получить плавную анимацию, умножив длительность на некоторый коэффициент, например 4.a.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
— Джефферсон Энрике С. Соарес

10
@Alioo, импорт android.view.animation.Transformation;
— Джомия

5
Работает отлично! У меня были проблемы с измеренной высотой, так как я хотел расширить фиксированный элемент dp, поэтому я изменил меру на, v.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));и v.getLayoutParams().height = interpolatedTime == 1 ? targetHeight : (int)(targetHeight * interpolatedTime);это сработало для меня!
— vkislicins

140

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

5
В коде есть опечатка: имя метода «initalize» должно быть «initialize», иначе оно не будет вызвано. ;) Я бы порекомендовал использовать @Override в будущем, так что этот тип опечаток будет пойман компилятором.
— Лорн Лалиберте

4
Я делаю следующее: "DropDownAnim anim = новый DropDownAnim (grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start ();" но это не работает Я установил некоторые точки останова на applyTransformation, но они никогда не достигаются
— Пауло Сезар

5
Упс, заставил его работать, это view.startAnimation (a) ... Производительность не очень хорошая, но она работает :)
— Пауло Сезар

3
@IamStalker В этой ситуации вам, вероятно, следует выполнить инициализацию с двумя переменными: начальная высота и конечная высота. Затем измените на: if (down) {newHeight = (int) (((ОкончаниеHeight-начальныйHeight) * interpolatedTime) + начальныйHeight); } else {newHeight = (int) (((ОкончаниеHeight-начальныйHeight) * (1 - interpolatedTime)) + начальныйHeight); }
— Сет Нельсон

3
@Seth Я думаю, что newHeight может быть просто (int) (((targetHeight -startingHeight) * interpolatedTime) + начальныйВысота) независимо от направления, если для параметра initialHeight установлено значение initialize ().
— Гиоргос Килафас

138

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

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

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


2
+1, сейчас я ищу скорость: продолжительность animateLayoutChanges
— Тушар Пандей

9
Анимация изменений макета: developer.android.com/training/animation/layout.html
— ccpizza,

Не работает после нажатия кнопки назад. Какие-либо предложения?
— Хасан Тарек

4
Это прекрасно работает для расширенной анимации, но для свертывания анимация происходит после сжатия родительского макета.
— shine_joseph

3
@shine_joseph да, я использую это в обзоре переработчика, и когда рушится выглядит очень странно: /
— AmirG

65

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


Хотя это работает, это зависит от настроек разработчика (длительности анимации). Если он отключен, анимация не будет отображаться.
— CoolMind

Да, но это может быть или не быть проблемой. Зависит от вашего приложения. Например, вы можете легко сделать длительность анимации пропорциональной разложенному / свернутому размеру с помощью простых изменений. Настраиваемая продолжительность анимации дает вам немного больше свободы.
— Джеральдо Нето

Развернуть анимацию не работает. это похоже на анимацию коллапса.
— Ахамадулла Сайкат

39

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

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

и для разрушения:

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

как запустить анимацию .. View.startAnimation (anim); не похоже на работу
— Махендран

это точно, как я запускаю анимацию. У вас работают другие анимации?
— ChristophK

1
Пошел с этим подходом, работает как шарм и нет необходимости реализовывать то, что уже было реализовано.
— erbsman

15
Это не сдвигает виды вниз под ним во время анимации и выглядит так, как будто растягивает анимированный вид от 0 до> h.

5
Кстати, анимации просмотра отлично работают для масштабирования: oView.animate (). ScaleY (0) для вертикального свертывания; oView.animate (). scaleY (1), чтобы открыть (обратите внимание, что доступно только SDK 12 и выше).
— Кирк Б.

27

@Tom Esterez's ответ , но обновляется , чтобы использовать 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);
    }

1
что такое addHeight и DURATION_MULTIPLIER?
— MidasLefko

Забудьте об этом, addHeight на тот случай, если вам нужна дополнительная высота в расширении (вероятно, нет), а DURATION_MODIFIER - просто модификатор скорости на случай, если вы хотите ускорить / замедлить анимацию.
— Эрик Б

1
Работает отлично! При использовании TextView возникает одна небольшая задержка с одним словом в последней строке. И не могли бы вы объяснить, что делает PathInterpolator ..?
— Йеннсара

EaseInOutQuart делает анимацию сначала медленной, затем быстрой, а затем медленной в конце для очень естественного ощущения. Об этом подробно говорят
— Эрик Б

1
Я попробовал ваш метод, но когда анимация заканчивается, мой вид больше не виден.
— Аман Верма

26

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

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

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


3
+1, даже если он назван некрасивым, он работает для представления, где мы еще не знаем его размер (например, если мы добавляем вновь созданное представление (размер которого FILL_PARENT) к родителю и хотели бы анимировать этот процесс, в том числе анимация роста родительского размера).
— Вит Худенко

Кстати, похоже, что в View.onMeause(widthMeasureSpec, heightMeasureSpec)вызове произошла небольшая ошибка , поэтому спецификации ширины и высоты следует поменять местами.
— Вит Худенко

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

1
У меня есть эта проблема ... содержимое в разборном виде исчезает при расширении. У меня есть Recycler View, который исчезает при расширении этого представления. @LenaYan
— Акшай Махаджан

21

Если вы не хотите, чтобы развернуть или свернуть полностью - вот простая анимация высоты -

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

13

Я адаптировал принятый в настоящее время ответ Тома Эстереза , который работал, но имел изменчивую и не очень плавную анимацию. Мое решение в основном заменяет на Animationa ValueAnimator, который может быть снабжен Interpolatorвашим выбором для достижения различных эффектов, таких как перерегулирование, отскок, ускорение и т. Д.

Это решение прекрасно работает с видами, которые имеют динамическую высоту (т.е. используют WRAP_CONTENT), так как сначала измеряют фактическую требуемую высоту, а затем анимируют до этой высоты.

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

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

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

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

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

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

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

Вы тогда просто позвоните expand( myView );или collapse( myView );.


Спасибо. Вы также можете добавить ситуацию, когда минимальная высота не равна 0.
— CoolMind

я работаю на меня для линейного выхода
— Роджер

Просто исправил используемые параметры v.measure()и теперь он работает отлично. Спасибо!
— Shahood ul Hassan

9

Использование функций расширения Kotlin - это проверенный и самый короткий ответ

Просто вызовите animateVisibility (разверните / сверните) в любом представлении.

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

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

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

    animateView(view, initialHeight, targetHeight)
}

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

    animateView(view, initialHeight, targetHeight)
}

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

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

хотел опубликовать тот же ответ :) Жаль, что это похоронено так глубоко здесь.
— muetzenflo

@muetzenflo Если ответ будет все больше и больше людей, он придет. :)
— Раджкиран

Мне нравилось это решение до тех пор, пока я не понял, что если есть текстовое представление с несколькими строками высотой wrap_content, то при раскрытии текстовое представление будет показывать только одну строку. Я пытаюсь исправить сейчас
— olearyj234

Я попробовал это, но анимация не выглядит плавной. Для расширения, весь текстовый вид появляется на короткое время, а затем воспроизводится анимация. При свертывании текстовое представление по какой-то причине сразу же разворачивается сразу же после свертывания. Есть идеи, что я делаю не так?
— Анчит Ачарья

7

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

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

... который вызывает метод ниже и позволяет ему выяснить, что делать после каждого onClick () ...

public static void expandCollapse(View view) {

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

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

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

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

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

}

Я пробовал этот код, но для его работы на нескольких видах, вы должны прокрутить. Любая идея, как я могу это исправить? stackoverflow.com/q/43916369/1009507
— Саммюкави

@Ukavi Я использую это с несколькими представлениями, и это прекрасно работает внутри ScrollView.
— mjp66

Как насчет просмотра переработчика?
— Саммюкави

@Ukavi еще не было необходимости использовать его в обзоре переработчиков, но я не понимаю, почему это не сработает. Вам придется немного поэкспериментировать с этим;)
— mjp66

6

Я хотел бы добавить кое-что к очень полезному ответу выше . Если вы не знаете, какую высоту вы получите, поскольку ваши представления .getHeight () возвращает 0, вы можете сделать следующее, чтобы получить высоту:

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

Где DUMMY_HIGH_DIMENSIONS - это ширина / высота (в пикселях), в которой ваш вид ограничен ... иметь это огромное число разумно, если представление инкапсулировано с ScrollView.


6

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

}


Есть ли хитрость, чтобы заставить это работать? Класс получает правильную исходную и целевую ширину, но мои представления не меняются. Я использую resizeAnim.start(). Также пробовал с и безsetFillAfter(true)
— Бен Кейн

Понял. Пришлось позвонить .startAnimation(resizeAnim)на вид.
— Бен Кейн

6

Для плавной анимации используйте 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);
}

5

комбинированные решения от @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);

4

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

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

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


4

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

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

4

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


3

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


Я согласен, но как получить initialHeight (требуется моей анимацией), если я делаю это?
— Том Эстерез

Вы пытались на самом деле просто сохранить начальную высоту в initialize, установить видимое там представление, а затем установить v.getLayoutParams (). Height = 0; сразу после, все в инициализации?
— Мика Хейнлайн

Да, если я это сделаю, метод инициализации вызывается с height = 0
— Том Эстерез

3

Это было мое решение, мое 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);
        }
    });

}

3

Это правильное рабочее решение, я проверил его:

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

Collapse:

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

    mAnimator = slideAnimator(finalHeight, 0);

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

        }

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

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    mAnimator.start();
}

Значение аниматора:

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

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

Представление v - это анимированное представление, PARENT_VIEW - представление контейнера, содержащее это представление.


2

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

2

Лучшее решение для развёртывания / свёртывания представлений:

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

Хотя это работает, это также зависит от настроек разработчика (длительности анимации). И отполируйте свой код, удалите лямбда-функцию и переформатируйте onCheckedChanged.
— CoolMind

Почему достаточно вызвать requestLayout только для v после изменения v LayoutParams? Я подумал, что необходимо вызвать requestLayout для родителя v
— vlazzle

2

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


1

Я создал версию, в которой вам не нужно указывать высоту макета, поэтому его намного проще и понятнее использовать. Решение состоит в том, чтобы получить высоту в первом кадре анимации (она доступна в тот момент, по крайней мере, во время моих тестов). Таким образом, вы можете предоставить 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);
    }
}

1

Используйте ValueAnimator:

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


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

В моем случае ничего не делает. Также вы можете облегчить ваш код, свернув 2 строки в mainView.getLayoutParams().height = height.
— CoolMind

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

1
/**
 * 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();
                    }

1

Основываясь на решениях @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;

    view.setVisibility(View.VISIBLE);

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

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

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

Ну, через 3 года я снова протестировал несколько решений, но только мое работало правильно.
— CoolMind
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.