假设我有一个垂直线性布局:

[v1]
[v2]

默认情况下,v1已经可见= GONE。我想用一个展开动画显示v1,同时下推v2。

我是这样做的:

Animation a = new Animation()
{
    int initialHeight;

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final int newHeight = (int)(initialHeight * interpolatedTime);
        v.getLayoutParams().height = newHeight;
        v.requestLayout();
    }

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

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

但是用这个解决方案,当动画开始时我有一个眨眼。我认为这是由于v1在应用动画之前显示全尺寸。

用javascript,这是一行jQuery!android有什么简单的方法吗?


当前回答

根据@Tom Esterez和@Seth Nelson的解决方案,我对它们进行了简化。与原始解决方案一样,它不依赖于开发人员选项(动画设置)。

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

其他回答

我认为最简单的解决方案是设置android:animateLayoutChanges="true"到你的LinearLayout,然后通过设置其可见性来显示/隐藏视图。工作就像一个魅力,但你无法控制动画持续时间

利用Kotlin扩展函数这是测试和最短的答案

在任何视图上调用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()
}

你不需要实现动画, 你可以输入android:animateLayoutChanges="true" 以便每当子视图布局发生变化时, 它将是动画……

我想对上面非常有用的答案补充一些东西。如果你不知道你最终得到的高度,因为你的views .getHeight()返回0,你可以做下面的事情来获得高度:

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

其中DUMMY_HIGH_DIMENSIONS是宽度/高度(以像素为单位),您的视图被限制为…当视图被ScrollView封装时,有这么大的数字是合理的。

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