Android : 확장 / 축소 애니메이션 a = new Animation() {

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

[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의 한 줄입니다! 안드로이드로 이것을 수행하는 간단한 방법은 무엇입니까?



답변

이 질문이 인기를 얻었으므로 실제 솔루션을 게시했습니다. 가장 큰 장점은 애니메이션을 적용하기 위해 확장 된 높이를 알 필요가 없으며 뷰가 확장되면 컨텐츠가 변경되면 높이를 조정한다는 것입니다. 그것은 나를 위해 잘 작동합니다.

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


답변

나는 매우 유사한 애니메이션이라고 생각하고 우아한 해결책을 찾았습니다. 이 코드는 항상 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;
    }
}

답변

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

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

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


답변

나는 제대로 작동하지 않는 @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에게 감사드립니다!


답변

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

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

그리고 붕괴를 위해 :

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

답변

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

답변

좋아, 방금 매우 못생긴 해결책을 찾았습니다.

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

더 나은 솔루션을 제안하십시오!