Android: анімація розгортання / згортання


449

Скажімо, у мене вертикальний лінійний макет із:

[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 в коментарях, ви можете отримати більш плавну анімацію, змінивши тривалість (і, отже, швидкість) анімації. В даний час вона встановлена ​​зі швидкістю 1dp / мс


13
v.measure (MeasureSpec.makeMeasureSpec (LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec (LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); У деяких випадках (мій - ListView) ця невідповідність призводить до неправильного значення targtetHeight
Johnny Doe

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 = максимальна висота перегляду, і вниз = булева, яка визначає напрямок (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" має бути "ініціалізовано", інакше воно не отримає виклик. ;) Я рекомендую в подальшому використовувати @Override, щоб компілятор потрапив до цього типу.
Lorne Laliberte

4
Я роблю наступне: "DropDownAnim anim = new DropDownAnim (grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start ();" але це не працює. Я розмістив кілька точок перерви у застосуванні Трансформації, але вони ніколи не досягаються
Пауло Сезар

5
Ой, змусив його працювати, це view.startAnimation (a) ... Продуктивність не дуже хороша, але вона працює :)
Пауло Сезар

3
@IamStalker У цій ситуації вам, ймовірно, слід ініціалізуватися з двома змінними, починаючи з Heeight і закінчуючи Heeight. Потім змініть на: якщо (вниз) {newHeight = (int) (((endingHeight-startHeight) * interpolatedTime) + startHeight); } else {newHeight = (int) (((endingHeight-startHeight) * (1 - interpolatedTime)) + startHeight); }
Сет Нельсон

3
@Seth Я думаю, що newHeight може бути просто (int) (((targetHeight -startingHeight) * interpolatedTime) + startHeight), незалежно від напрямку, доки встановлений startHeight при ініціалізації ().
Giorgos Kylafas

138

Я сьогодні натрапив на ту ж проблему, і, мабуть, справжнє рішення цього питання полягає в цьому

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

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


2
+1, Зараз я шукаю Швидкість: тривалість animateLayoutChanges
Tushar Pandey

9
Зміни в
анімаційному

Це не працює після натискання кнопки назад. Будь-які пропозиції?
Хасан Тарек

4
Це ідеально підходить для розширення анімації, але для колапсу анімація відбувається після зменшення батьківського макета.
shine_joseph

3
@shine_joseph Так, я використовую це всередині рециркулятора і коли згортається, виглядає дуже дивно: /
AmirG

65

Я взяв @LenaYan «s рішення , що не працює належним чином для мене ( бо він був перетворюючи вид на вид на 0 висоти перед руйнуванням і / або розширення ) і вніс деякі зміни.

Тепер він чудово працює , взявши попередній зріст View і почавши розширюватися з цим розміром. Згортання те саме.

Ви можете просто скопіювати і вставити код нижче:

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); не здається, що працює
Mahendran

це точно, як я запускаю анімацію. Чи працюють інші анімації для вас?
КрістофК

1
Пішов з таким підходом, працює як шарм і не потрібно реалізовувати те, що вже було реалізовано.
ербсман

15
Це не висуває подання під ним під час анімації і видається так, ніби розтягує анімоване подання від 0 -> год.

5
Btw, перегляд анімації відмінно підходить для масштабування: oView.animate (). ScaleY (0) для вертикального згортання; oView.animate (). scaleY (1), щоб відкрити (зауважте, що доступно лише sdk 12 і вище).
Кірк Б.

27

@Tom Esterez в відповідь , але оновлюється , щоб використовувати view.measure () правильно на Android getMeasuredHeight повертає неправильні значення!

    // http://easings.net/
    Interpolator easeInOutQuart = PathInterpolatorCompat.create(0.77f, 0f, 0.175f, 1f);

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

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

        Animation animation = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {

               view.getLayoutParams().height = interpolatedTime == 1
                    ? ViewGroup.LayoutParams.WRAP_CONTENT
                    : (int) (targetHeight * interpolatedTime);

            view.requestLayout();
        }

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

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

        return animation;
    }

    public static Animation collapse(final View view) {
        final int initialHeight = view.getMeasuredHeight();

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

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

        a.setInterpolator(easeInOutQuart);

        int durationMillis = computeDurationFromHeight(view);
        a.setDuration(durationMillis);

        view.startAnimation(a);

        return a;
    }

    private static int computeDurationFromHeight(View view) {
        // 1dp/ms * multiplier
        return (int) (view.getMeasuredHeight() / view.getContext().getResources().getDisplayMetrics().density);
    }

1
що таке addHeight та DURATION_MULTIPLIER?
MidasLefko

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

1
Чудово працює! Одне невелике відставання виникає під час використання TextView лише одним словом в останньому рядку. А ви могли б пояснити, що робить PathInterpolator? ..
yennsarah

EasyInOutQuart спочатку робить анімацію повільною, потім швидкою, а потім повільною в кінці для дуже природного почуття. Вони розповідають про це глибоко тут easings.net
Ерік Б

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, який зникає при розширенні цього подання. @LenaYan
Акшай Махаджан

21

Якщо ви не хочете розширюватись або згортатись до кінця - ось проста HeightAnimation -

import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class HeightAnimation extends Animation {
    protected final int originalHeight;
    protected final View view;
    protected float perValue;

    public HeightAnimation(View view, int fromHeight, int toHeight) {
        this.view = view;
        this.originalHeight = fromHeight;
        this.perValue = (toHeight - fromHeight);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        view.getLayoutParams().height = (int) (originalHeight + perValue * interpolatedTime);
        view.requestLayout();
    }

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

Використання:

HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());
heightAnim.setDuration(1000);
view.startAnimation(heightAnim);

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

я працюю на мене для linearlayout
Роджер,

Просто виправив парами, які використовуються в v.measure()і зараз він працює чудово. Дякую!
Шейція уль Хассан

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
sammyukavi

@Ukavi Я використовую це з кількома переглядами, і він чудово працює у ScrollView.
mjp66

Що з рециркулятором?
sammyukavi

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

6

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

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

Там, де DUMMY_HIGH_DIMENSIONS - ширина / висота (у пікселях), ваш погляд обмежений… такою великою є кількість розумних, коли вигляд інкапсульований ScrollView.


6

Це фрагмент, який я використовував для зміни розміру ширини перегляду (LinearLayout) за допомогою анімації.

Код повинен розширюватися або зменшуватися відповідно до цільового розміру. Якщо ви хочете заповнити прозору ширину, вам доведеться передати батьківський .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

Для плавної анімації, будь ласка, використовуйте обробник методом запуску ..... І насолоджуйтесь розгортанням / згортанням анімації

    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 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, щоб висота макета дорівнювала нулю прямо до початку анімації. Ви хочете ініціалізувати налаштування, щоб виглядати як перший кадр анімації перед запуском анімації.


Я погоджуюся, але як отримати початковий висоту (вимагає моя анімація), якщо я це роблю?
Том Естерез

Ви спробували насправді просто зберегти початкову висоту при ініціалізації, встановивши вигляд там, а потім встановивши v.getLayoutParams (). Висота = 0; безпосередньо після, все в ініціалізації?
Міхей Хайнлайн

Так, якщо я це роблю, метод ініціалізації викликається з висотою = 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

Це правильне робоче рішення, я перевірив його:

Розгорнути:

private void expand(View v) {
    v.setVisibility(View.VISIBLE);

    v.measure(View.MeasureSpec.makeMeasureSpec(PARENT_VIEW.getWidth(), View.MeasureSpec.EXACTLY),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));

    final int targetHeight = v.getMeasuredHeight();

    mAnimator = slideAnimator(0, targetHeight);
    mAnimator.setDuration(800);
    mAnimator.start();
}

Згорнути:

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

    mAnimator = slideAnimator(finalHeight, 0);

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

        }

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

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
    mAnimator.start();
}

Значення аніматора:

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

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

Вид 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 v? Я подумав, що потрібно буде викликати 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

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

У конструкторі також є один невеликий злом - нижній край встановлений на -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();

У моєму випадку нічого не робить. Також ви можете полегшити свій код, зібравши в нього два рядки 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 (топ-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.