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

Скажем, у меня есть вертикальный 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! Любой простой способ сделать это с помощью андроида?

Ответ 1

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

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 дп/мс

Ответ 2

Я пытался сделать то, что, по моему мнению, было очень похожей анимацией и нашел элегантное решение. Этот код предполагает, что вы всегда переходите от 0- > h или h- > 0 (h - максимальная высота). Три параметра конструктора: view = анимированный вид (в моем случае, веб-просмотр), targetHeight = максимальная высота представления и down = boolean, который определяет направление (true = expand, false = collapsing).

public class DropDownAnim extends Animation {
    private final int targetHeight;
    private final View view;
    private final boolean down;

    public DropDownAnim(View view, int targetHeight, boolean down) {
        this.view = view;
        this.targetHeight = targetHeight;
        this.down = down;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        int newHeight;
        if (down) {
            newHeight = (int) (targetHeight * interpolatedTime);
        } else {
            newHeight = (int) (targetHeight * (1 - interpolatedTime));
        }
        view.getLayoutParams().height = newHeight;
        view.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth,
            int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
    }

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

Ответ 3

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

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

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

Ответ 4

Я взял @LenaYan решение, которое не срабатывало должным образом для меня (потому что он преобразовывал представление "Вид в 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 за исходный код!

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

Если вы не хотите расширять или сворачивать всю дорогу - вот простая версия 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);

Ответ 10

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

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

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

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

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

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

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

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

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

Затем вы просто вызываете expand( myView ); или collapse( myView );.

Ответ 11

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

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

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

Ответ 12

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

}

Ответ 13

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

Ответ 14

Добавление в Tom Esterez отличный ответ и Erik B отличное обновление к нему, я думал, что буду публиковать свои собственные выводы, уплотнять методы расширения и заключения контрактов в один. Таким образом, вы можете, например, выполнить такое действие...

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

}

Ответ 15

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

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

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

Ответ 16

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

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

Ответ 17

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

Ответ 18

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

Ответ 19

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

Ответ 20

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

Ответ 21

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

}

Ответ 22

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

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

Значение Animator:

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 - это представление контейнера, содержащее представление.

Ответ 23

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

Ответ 24

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

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

Ответ 25

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

Ответ 26

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

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

Ответ 27

Использовать 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();

Ответ 28

Основываясь на решениях @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);
            } else {
                view.getLayoutParams().height = (int) (initialHeight + distance * interpolatedTime);
                view.requestLayout();
            }
        }

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

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

Ответ 29

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

Ответ 30

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