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

[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有什么简单的方法吗?


当前回答

我已经使用了相同的代码块,已经在接受的答案,但它不会像在android 9一样工作,所以更新措施根据这一点

v.measure(MeasureSpec.makeMeasureSpec(parentView.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(parentView.getWidth(), MeasureSpec.AT_MOST));

约束的工作方式在android 9中有所不同。

其他回答

我想对上面非常有用的答案补充一些东西。如果你不知道你最终得到的高度,因为你的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();
}

我今天也遇到了同样的问题,我想这个问题的真正解决方案是这样的

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

您必须为所有涉及移位的最顶层布局设置此属性。如果你现在将一个布局的可见性设置为GONE,另一个将在消失的布局释放它时占据空间。会有一个默认的动画,这是某种“淡出”,但我认为你可以改变这个-但最后一个我没有测试,现在。


如果在RecyclerView项目中使用这个,在onBindViewHolder中设置视图的可见性来展开/折叠,并调用notifyItemChanged(position)来触发转换。

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        ...

        holder.list.visibility = data[position].listVisibility
        holder.expandCollapse.setOnClickListener {
            data[position].listVisibility = if (data[position].listVisibility == View.GONE) View.VISIBLE else View.GONE
            notifyItemChanged(position)
        }
    }

如果你在onBindViewHolder中执行昂贵的操作,你可以使用notifyItemChanged(位置,有效载荷)优化部分更改

private const val UPDATE_LIST_VISIBILITY = 1

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
        if (payloads.contains(UPDATE_LIST_VISIBILITY)) {
            holder.list.visibility = data[position].listVisibility
        } else {
            onBindViewHolder(holder, position)
        }
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        ...

        holder.list.visibility = data[position].listVisibility
        holder.expandCollapse.setOnClickListener {
            data[position].listVisibility = if (data[position].listVisibility == View.GONE) View.VISIBLE else View.GONE
            notifyItemChanged(position, UPDATE_LIST_VISIBILITY)
        }
    }

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

是的,我同意上面的评论。事实上,它似乎是正确的(或者至少是最简单的?)事情要做的是指定(在XML中)一个初始布局高度“0px”——然后你可以传递另一个参数“toHeight”(即“最终高度”)到你的自定义动画子类的构造函数,例如,在上面的例子中,它看起来像这样:

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

不管怎样,希望这能有所帮助!:)