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, другой займет пространство, поскольку исчезающий освобождает его. Будет анимация по умолчанию, которая будет своего рода "исчезать", но я думаю, что вы можете изменить это - но последний, который я пока не тестировал.

Mr.Fu
источник
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);
ChristophK
источник
как запустить анимацию .. 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();
}
LenaYan
источник
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 );.

Магнус W
источник
Спасибо. Вы также можете добавить ситуацию, когда минимальная высота не равна 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);

}
mjp66
источник
Я пробовал этот код, но для его работы на нескольких видах, вы должны прокрутить. Любая идея, как я могу это исправить? 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.

gardarh
источник
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;
}

}

Codewarrior
источник
Есть ли хитрость, чтобы заставить это работать? Класс получает правильную исходную и целевую ширину, но мои представления не меняются. Я использую 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);
Flux
источник
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);
        }
    });

}
Jorgesys
источник
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();
                    }
Amardeep
источник
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);
}
CoolMind
источник
Ну, через 3 года я снова протестировал несколько решений, но только мое работало правильно.
CoolMind