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! 用android做任何简单的方法吗?
30个解决方案
665 votes
我看到这个问题变得流行,所以我发布了我的实际解决方案。 主要优点是您不必知道应用动画的扩展高度,并且一旦展开视图,它会在内容更改时调整高度。 这对我很有效。
public static void expand(final View v) {
v.measure(LayoutParams.MATCH_PARENT, 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);
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;
}
};
// 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;
}
};
// 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
Tom Esterez answered 2019-03-31T02:27:05Z
139 votes
我试图做我认为是一个非常相似的动画,并找到了一个优雅的解决方案。 此代码假定您始终从0-> h或h-> 0(h是最大高度)。 三个构造函数参数是view =要动画的视图(在我的例子中是webview),targetHeight =视图的最大高度,down =一个指定方向的布尔值(true = expanded,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;
}
}
Seth Nelson answered 2019-03-31T02:27:30Z
120 votes
我今天偶然发现了同样的问题,我想这个问题的真正解决方案就是这个
android:animateLayoutChanges="true"
...
/>
您必须为所有最顶层布局设置此属性,这些布局涉及移位。 如果您现在将一个布局的可见性设置为GONE,另一个将占用空间,因为消失的布局将释放它。 将会有一个默认动画,它是某种“淡出”,但我认为你可以改变它 - 但是现在我还没有测试过的最后一个。
Mr.Fu answered 2019-03-31T02:28:01Z
54 votes
我采用了@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的初始代码!
Geraldo Neto answered 2019-03-31T02:28:55Z
39 votes
另一种方法是使用具有以下缩放因子的缩放动画进行扩展:
ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);
和折叠:
ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
ChristophK answered 2019-03-31T02:29:26Z
25 votes
好的,我刚刚发现了一个非常难看的解决方案:
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;
}
随意提出更好的解决方案!
Tom Esterez answered 2019-03-31T02:29:57Z
24 votes
@Tom Esterez的答案,但更新为正确使用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);</