Android : 확장 / 축소 애니메이션


449

수직 선형 선형 레이아웃이 있다고 가정 해 보겠습니다.

[v1]
[v2]

기본적으로 v1은 눈에 띄게 = 사라졌습니다. 확장 애니메이션으로 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이 전체 크기로 표시되어 발생한다고 생각합니다.

자바 스크립트를 사용하면 jQuery의 한 줄입니다! 안드로이드로 이것을 수행하는 간단한 방법은 무엇입니까?

답변:


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 / ms의 속도로 설정되었습니다


13
v.measure (MeasureSpec.makeMeasureSpec (LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec (LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); 어떤 경우에는 (my-ListView)이 불일치가 잘못된 targtetHeight 값을 가져옵니다
Johnny Doe

12
@Tom Esterez 이것은 작동하지만 매끄럽지 않습니다. 원활하게하기위한 추가 작업이 있습니까?
acntwww

9
@acntwww 지속 시간에 4와 같은 요소를 곱한 부드러운 애니메이션을 얻을 수 있습니다.a.setDuration(((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density)) * 4)
Jefferson Henrique C. Soares

10
@Alioo, import android.view.animation.Transformation;
Jomia

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는 최대 높이 임)에서 시작한다고 가정합니다. 3 개의 생성자 매개 변수는 view = 애니메이션 될 뷰 (내 경우에는 webview), targetHeight = 뷰의 최대 높이 및 down = 방향을 지정하는 부울 (true = 확장, false = 축소)입니다.

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

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

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

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

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

5
코드에 오타가 있습니다. "initalize"메소드 이름은 "initialize"여야합니다. 그렇지 않으면 호출되지 않습니다. ;) 장래에 @Override를 사용하여 컴파일러에 의해 오타가 발생하는 것이 좋습니다.
Lorne Laliberte

4
"DropDownAnim 애니메이션 = 새 DropDownAnim (grid_titulos_atual, GRID_HEIGHT, true); anim.setDuration (500); anim.start (); 하지만 작동하지 않습니다. applyTransformation에 몇 가지 중단 점을 두었지만 도달하지 못했습니다.
Paulo Cesar

5
본부, 업무에있어, 그것의 view.startAnimation (가) ... 성능이 아주 좋은 것은 아니지만이 : 작동
파울로 세자르

3
@IamStalker이 상황에서는 아마도 startingHeight와 endingHeight의 두 변수로 초기화해야합니다. if (down) {newHeight = (int) ((((endingHeight-startingHeight) * interpolatedTime) + startingHeight);로 변경하십시오. } else {newHeight = (int) ((((endingHeight-startingHeight) * (1-interpolatedTime)) + startingHeight); }
세스 넬슨

3
@Seth newHeight는 단순히 (Hit) (((targetHeight -startingHeight) * interpolatedTime) + startingHeight) 일 수 있습니다 .startHeight가 initialize ()에 설정되어있는 한 방향에 관계없이.
Giorgos Kylafas

138

나는 오늘 같은 문제를 우연히 발견 했으며이 질문에 대한 진정한 해결책은 이것이라고 생각합니다.

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

교대와 관련된 모든 최상위 레이아웃에 대해이 속성을 설정해야합니다. 이제 한 레이아웃의 가시성을 GONE으로 설정하면 다른 레이아웃이 사라짐에 따라 다른 레이아웃이 공간을 차지합니다. 일종의 "페이딩 아웃"인 기본 애니메이션이 있지만, 이것을 변경할 수 있다고 생각합니다. 그러나 지금까지 테스트하지 않은 마지막 애니메이션입니다.


2
+1, 이제 속도를 찾고 있습니다 : 지속 기간 of animateLayoutChanges
Tushar Pandey

9
에 애니메이션 레이아웃 변경 : developer.android.com/training/animation/layout.html
ccpizza

뒤로 버튼을 누른 후에는 작동하지 않습니다. 어떤 제안?
Hassan Tareq

4
이것은 애니메이션 확장에는 완벽하게 작동하지만 축소에 대해서는 부모 레이아웃이 축소 된 후에 애니메이션이 발생합니다.
shine_joseph

3
@shine_joseph yeah 나는 이것을 recyclerview 안에서 사용하고 있으며 붕괴가 정말로 이상하게 보일 때 : /
AmirG

65

나는 제대로 작동하지 않는 @LenaYan의 솔루션 을 가져 왔습니다 ( 붕괴 및 확장하기 전에 뷰를 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

예, 그러나 문제가 될 수도 있고 아닐 수도 있습니다. 응용 프로그램에 따라 다릅니다. 예를 들어, 간단한 변경만으로 애니메이션 지속 시간을 확장 / 축소 크기에 비례하게 만들 수 있습니다. 애니메이션 지속 시간을 설정할 수 있으면 조금 더 자유로울 수 있습니다.
Geraldo Neto

애니메이션이 작동하지 않습니다. 축소 애니메이션처럼 보입니다.
Ahamadullah Saikat

39

다른 방법으로는 다음과 같은 확장 요소를 사용하여 확장 애니메이션을 사용하는 것입니다.

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

그리고 붕괴를 위해 :

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

애니메이션을 시작하는 방법. View.startAnimation (anim); 작동하지 않는 것
Mahendran

내가 애니메이션을 시작하는 방법은 과실입니다. 다른 애니메이션이 효과가 있습니까?
ChristophK

1
이 접근 방식을 사용하면 매력처럼 작동하며 이미 구현 된 것을 구현할 필요가 없습니다.
erbsman

15
애니메이션 중 뷰 아래를 아래로 내리지 않고 0-> h에서 애니메이션 뷰를 늘리는 것처럼 나타납니다.

5
Btw, 뷰 애니메이션은 스케일링에 효과적입니다. oView.animate (). scaleY (0)은 세로로 축소됩니다. oView.animate (). scaleY (1)을 열려면 SDK 12 이상에서만 사용할 수 있습니다.
Kirk B.

27

@Tom Esterez의 답변 이지만 Android에 따라 view.measure ()를 올바르게 사용하도록 업데이트되었습니다. 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는 애니메이션 속도 / 느리게하려는 경우 속도 수정 자 일뿐입니다.
Erik B

1
잘 작동합니다! 마지막 줄에 단 하나의 단어로 TextView를 사용하는 동안 작은 지연이 발생합니다. 그리고 당신은 PathInterpolator가 무엇을하는지 설명 할 수 있습니까?
yennsarah

easeInOutQuart는 애니메이션을 처음에는 느리게 한 다음 느리게 한 다음 끝에서 느리게하여 매우 자연스러운 느낌을줍니다. 그들은 여기에 깊이 얘기 easings.net
에릭 B

1
나는 당신의 방법을 시도했지만 애니메이션이 끝날 때마다 내보기가 더 이상 보이지 않습니다.
Aman Verma

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 인 뷰)를 부모에 추가하고 애니메이션하려는 경우) 이 과정 (부모 크기 증가 애니메이션 포함).
Vit Khudenko

BTW, View.onMeause(widthMeasureSpec, heightMeasureSpec)호출에 약간의 오류가있는 것처럼 보이 므로 너비 및 높이 사양을 바꿔야합니다.
Vit Khudenko

22
public static void expand(final View v, int duration, int targetHeight) {
        v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        v.getLayoutParams().height = 0;
        v.setVisibility(View.VISIBLE);
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                v.getLayoutParams().height = (int) animation.getAnimatedValue();
                v.requestLayout();
            }
        });
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.setDuration(duration);
        valueAnimator.start();
    }
public static void collapse(final View v, int duration, int targetHeight) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(0, targetHeight);
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            v.getLayoutParams().height = (int) animation.getAnimatedValue();
            v.requestLayout();
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(duration);
    valueAnimator.start();
}

1
이 문제가 있습니다 ... 축소 할 때 축소 가능한보기의 내용이 사라집니다. 이보기를 확장하면 사라지는 Recycler View가 있습니다. @LenaYan
Akshay Mahajan

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

나는 Tom Esterez가 현재 받아 들인 대답을 수정 했습니다. 내 솔루션은 기본적으로 대체 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을 위해 나를 위해 일하고
Roger

사용 된 매개 변수를 수정하면 v.measure()완벽하게 작동합니다. 감사!
Shahood ul Hassan

9

Kotlin 확장 기능을 사용하여 테스트되었으며 가장 짧은 답변

모든 View에서 animateVisibility (expand / collapse)를 호출하십시오.

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 더 많은 사람들이 답을 찬성하면 답이 나옵니다. :)
Rajkiran

높이가 wrap_content 인 여러 줄의 텍스트보기가있는 경우이 솔루션을 좋아했습니다. 확장하면 텍스트보기에는 한 줄만 표시됩니다. 나는 지금 고치려고 노력하고있다
olearyj234

나는 이것을 시도했지만 애니메이션이 부드럽게 보이지 않습니다. 확장을 위해 전체 텍스트보기가 한 번에 잠깐 나타난 다음 애니메이션이 재생됩니다. 축소를 위해 어떤 이유로 축소 된 직후 텍스트보기가 즉시 다시 확장됩니다. 내가 뭘 잘못하고 있는지 알아?
Anchith Acharya

7

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

}

이 코드를 시도했지만 여러보기에서 견인 작업을 수행하려면 스크롤해야합니다. 어떻게 해결할 수 있습니까? stackoverflow.com/q/43916369/1009507
sammyukavi

@ Ukavi 여러보기와 함께 이것을 사용하고 있으며 ScrollView 내에서 잘 작동합니다.
mjp66

recyclerview에서 어떻습니까?
sammyukavi

@Ukavi는 아직 recyclerview에서 사용할 필요가 없지만 왜 작동하지 않는지 알 수 없습니다. 약간 직접 실험
해보아야합니다

6

의 매우 유용한 답변에 무언가를 추가하고 싶습니다 . 뷰 .getHeight ()가 0을 반환하기 때문에 높이를 모르는 경우 높이를 얻으려면 다음을 수행 할 수 있습니다.

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

여기서 DUMMY_HIGH_DIMENSIONS는 뷰가 너비 / 높이 (픽셀 단위)로 제한됩니다. ScrollView로 뷰를 캡슐화 할 때이 수를 갖는 것이 합리적입니다.


6

이것은 애니메이션으로 뷰의 너비 (LinearLayout)의 크기를 조정하는 데 사용 된 스 니펫입니다.

코드는 대상 크기에 따라 확장 또는 축소되어야합니다. fill_parent 너비를 원하면 플래그를 true로 설정하면서 부모 .getMeasuredWidth를 대상 너비로 전달해야합니다.

그것이 당신의 일부를 돕기를 바랍니다.

public class WidthResizeAnimation extends Animation {
int targetWidth;
int originaltWidth;
View view;
boolean expand;
int newWidth = 0;
boolean fillParent;

public WidthResizeAnimation(View view, int targetWidth, boolean fillParent) {
    this.view = view;
    this.originaltWidth = this.view.getMeasuredWidth();
    this.targetWidth = targetWidth;
    newWidth = originaltWidth;
    if (originaltWidth > targetWidth) {
        expand = false;
    } else {
        expand = true;
    }
    this.fillParent = fillParent;
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    if (expand && newWidth < targetWidth) {
        newWidth = (int) (newWidth + (targetWidth - newWidth) * interpolatedTime);
    }

    if (!expand && newWidth > targetWidth) {
        newWidth = (int) (newWidth - (newWidth - targetWidth) * interpolatedTime);
    }
    if (fillParent && interpolatedTime == 1.0) {
        view.getLayoutParams().width = -1;

    } else {
        view.getLayoutParams().width = newWidth;
    }
    view.requestLayout();
}

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

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

}


이것을 작동시키는 데 어떤 트릭이 있습니까? 클래스는 올바른 원래 너비와 목표 너비를 얻지 만 뷰의 크기는 조정되지 않습니다. 을 사용하고 resizeAnim.start()있습니다. 또한 함께 및없이 시도setFillAfter(true)
벤 케인

알았다. .startAnimation(resizeAnim)보기 를 불러야했다 .
벤 케인

6

부드러운 애니메이션을 위해서는 Handler를 run 메소드와 함께 사용하십시오.

    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"에 대한 다른 인수를 전달할 수 있습니다 ( 즉, "최종 높이")를 사용자 정의 애니메이션 하위 클래스의 생성자에 적용합니다 (예 : 위의 예와 같이).

    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의 레이아웃 높이가 0으로 설정되어 있는지 확인하십시오. 애니메이션을 시작하기 전에 애니메이션의 첫 번째 프레임처럼 보이도록 설정을 초기화하려고합니다.


동의하지만이 작업을 수행하면 initialHeight (내 애니메이션에 필요)를 얻는 방법은 무엇입니까?
톰 에스테 레즈

실제로 초기 높이를 초기화하여 저장하고 뷰를 표시 한 다음 v.getLayoutParams (). height = 0; 바로 다음에 모두 초기화됩니까?
미카 하인라인

예, 그렇게하면 initialize 메소드가 height = 0으로 호출됩니다.
Tom Esterez

3

이것은 내 솔루션을했다, 내가 ImageView에서 성장 100%200%두 개의 애니메이션 파일 내부에 사용하여, 자신의 원래 크기로 반환 및 res/anim/폴더를

anim_grow.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="1.0"
  android:toXScale="2.0"
  android:fromYScale="1.0"
  android:toYScale="2.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>

anim_shrink.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
 android:interpolator="@android:anim/accelerate_interpolator">
 <scale
  android:fromXScale="2.0"
  android:toXScale="1.0"
  android:fromYScale="2.0"
  android:toYScale="1.0"
  android:duration="3000"
  android:pivotX="50%"
  android:pivotY="50%"
  android:startOffset="2000" />
</set>

ImageView내 방법으로 보내기setAnimationGrowShrink()

ImageView img1 = (ImageView)findViewById(R.id.image1);
setAnimationGrowShrink(img1);

setAnimationGrowShrink() 방법:

private void setAnimationGrowShrink(final ImageView imgV){
    final Animation animationEnlarge = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_grow);
    final Animation animationShrink = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.anim_shrink);

    imgV.startAnimation(animationEnlarge);

    animationEnlarge.setAnimationListener(new AnimationListener() {         
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationShrink);
        }
    });

    animationShrink.setAnimationListener(new AnimationListener() {          
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation) {
            imgV.startAnimation(animationEnlarge);
        }
    });

}

3

이것은 올바른 작동 솔루션이며 테스트했습니다.

Exapnd :

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

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

    final int targetHeight = v.getMeasuredHeight();

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

무너짐:

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

View 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

v의 LayoutParams를 변경 한 후 v에서만 requestLayout을 호출하는 것으로 충분한 이유는 무엇입니까? 나는 V의 부모에 requestLayout를 호출 할 필요가있을 거라고 생각
vlazzle

2

약간의 왜곡으로 ViewPropertyAnimator를 사용할 수 있습니다. 축소하려면보기를 1 픽셀 높이로 조정 한 다음 숨 깁니다. 확장하려면 표시 한 다음 높이로 확장하십시오.

private void collapse(final View view) {
    view.setPivotY(0);
    view.animate().scaleY(1/view.getHeight()).setDuration(1000).withEndAction(new Runnable() {
        @Override public void run() {
            view.setVisibility(GONE);
        }
    });
}

private void expand(View view, int height) {
    float scaleFactor = height / view.getHeight();

    view.setVisibility(VISIBLE);
    view.setPivotY(0);
    view.animate().scaleY(scaleFactor).setDuration(1000);
}

피벗은 뷰의 스케일 위치를 알려줍니다. 기본값은 중간입니다. 기간은 선택 사항입니다 (기본값 = 1000). 보간기를 다음과 같이 사용하도록 설정할 수도 있습니다..setInterpolator(new AccelerateDecelerateInterpolator())


1

레이아웃 높이를 지정할 필요가없는 버전을 만들었으므로 사용하기가 훨씬 쉽고 깨끗합니다. 해결책은 애니메이션의 첫 번째 프레임에서 높이를 얻는 것입니다 (적어도 테스트 중에는 가능합니다). 이런 식으로 View에 임의의 높이와 하단 여백을 제공 할 수 있습니다.

생성자에는 하나의 작은 해킹이 있습니다. 아래쪽 여백은 -10000으로 설정되어 변환 전에 뷰가 숨겨져 있습니다 (깜박임 방지).

public class ExpandAnimation extends Animation {


    private View mAnimatedView;
    private ViewGroup.MarginLayoutParams mViewLayoutParams;
    private int mMarginStart, mMarginEnd;

    public ExpandAnimation(View view) {
        mAnimatedView = view;
        mViewLayoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        mMarginEnd = mViewLayoutParams.bottomMargin;
        mMarginStart = -10000; //hide before viewing by settings very high negative bottom margin (hack, but works nicely)
        mViewLayoutParams.bottomMargin = mMarginStart;
        mAnimatedView.setLayoutParams(mViewLayoutParams);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
            //view height is already known when the animation starts
            if(interpolatedTime==0){
                mMarginStart = -mAnimatedView.getHeight();
            }
            mViewLayoutParams.bottomMargin = (int)((mMarginEnd-mMarginStart) * interpolatedTime)+mMarginStart;
            mAnimatedView.setLayoutParams(mViewLayoutParams);
    }
}

1

ValueAnimator를 사용하십시오.

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


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

내 경우에는 아무것도하지 않습니다. 또한 2 줄을로 축소하여 코드를 쉽게 만들 수 있습니다 mainView.getLayoutParams().height = height.
CoolMind

1
public static void slide(View v, int speed, int pos) {
    v.animate().setDuration(speed);
    v.animate().translationY(pos);
    v.animate().start();
}

// slide down
slide(yourView, 250, yourViewHeight);
// slide up
slide(yourView, 250, 0);

1
/**
 * Animation that either expands or collapses a view by sliding it down to make
 * it visible. Or by sliding it up so it will hide. It will look like it slides
 * behind the view above.
 * 
 */
public class FinalExpandCollapseAnimation extends Animation
{
    private View mAnimatedView;
    private int mEndHeight;
    private int mType;
    public final static int COLLAPSE = 1;
    public final static int EXPAND = 0;
    private LinearLayout.LayoutParams mLayoutParams;
    private RelativeLayout.LayoutParams mLayoutParamsRel;
    private String layout;
    private Context context;

    /**
     * Initializes expand collapse animation, has two types, collapse (1) and
     * expand (0).
     * 
     * @param view
     *            The view to animate
     * @param type
     *            The type of animation: 0 will expand from gone and 0 size to
     *            visible and layout size defined in xml. 1 will collapse view
     *            and set to gone
     */
    public FinalExpandCollapseAnimation(View view, int type, int height, String layout, Context context)
    {
        this.layout = layout;
        this.context = context;
        mAnimatedView = view;
        mEndHeight = mAnimatedView.getMeasuredHeight();
        if (layout.equalsIgnoreCase("linear"))
            mLayoutParams = ((LinearLayout.LayoutParams) view.getLayoutParams());
        else
            mLayoutParamsRel = ((RelativeLayout.LayoutParams) view.getLayoutParams());
        mType = type;
        if (mType == EXPAND)
        {
            AppConstant.ANIMATED_VIEW_HEIGHT = height;
        }
        else
        {
            if (layout.equalsIgnoreCase("linear"))
                mLayoutParams.topMargin = 0;
            else
                mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
        }
        setDuration(600);
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        super.applyTransformation(interpolatedTime, t);
        if (interpolatedTime < 1.0f)
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT
                            + (-AppConstant.ANIMATED_VIEW_HEIGHT + (int) (AppConstant.ANIMATED_VIEW_HEIGHT * interpolatedTime));
                }
                mAnimatedView.setVisibility(View.VISIBLE);
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
                else
                    mLayoutParamsRel.height = mEndHeight - (int) (mEndHeight * interpolatedTime);
            }
            mAnimatedView.requestLayout();
        }
        else
        {
            if (mType == EXPAND)
            {
                if (layout.equalsIgnoreCase("linear"))
                {
                    mLayoutParams.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParams.topMargin = 0;
                }
                else
                {
                    mLayoutParamsRel.height = AppConstant.ANIMATED_VIEW_HEIGHT;
                    mLayoutParamsRel.topMargin = convertPixelsIntoDensityPixels(36);
                }
                mAnimatedView.setVisibility(View.VISIBLE);
                mAnimatedView.requestLayout();
            }
            else
            {
                if (layout.equalsIgnoreCase("linear"))
                    mLayoutParams.height = 0;
                else
                    mLayoutParamsRel.height = 0;
                mAnimatedView.setVisibility(View.GONE);
                mAnimatedView.requestLayout();
            }
        }
    }

    private int convertPixelsIntoDensityPixels(int pixels)
    {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        return (int) metrics.density * pixels;
    }
}

클래스는 다음과 같은 방법으로 호출 할 수 있습니다

   if (findViewById(R.id.ll_specailoffer_show_hide).getVisibility() == View.VISIBLE) {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown_up);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.COLLAPSE,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    } else {
                        ((ImageView) findViewById(R.id.iv_specialhour_seemore)).setImageResource(R.drawable.white_dropdown);

                        FinalExpandCollapseAnimation finalExpandCollapseAnimation = new FinalExpandCollapseAnimation(
                                findViewById(R.id.ll_specailoffer_show_hide),
                                FinalExpandCollapseAnimation.EXPAND,
                                SpecialOfferHeight, "linear", this);
                        findViewById(R.id.ll_specailoffer_show_hide)
                                .startAnimation(finalExpandCollapseAnimation);
                        ((View) findViewById(R.id.ll_specailoffer_show_hide).getParent()).invalidate();
                    }

1

@Tom Esterez와 @Seth Nelson (상위 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.