本文共 40163 字,大约阅读时间需要 133 分钟。
假设我有一个带有以下内容的垂直linearLayout:
[v1][v2]
默认情况下,v1的可见值= GONE。 我想用扩展动画显示v1并同时按下v2。
我尝试过这样的事情:
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的一行! 任何简单的方法来用吗?
我看到这个问题开始流行,所以我发布了实际的解决方案。 主要优点是您不必知道扩展的高度即可应用动画,并且一旦视图扩展后,如果内容更改,视图就会适应高度。 这对我很有效。
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
这是我的解决方案。 我认为这更简单。 它仅扩展视图,但可以轻松扩展。
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; }}
确保在动画开始之前将v1设置为具有零布局高度。 您希望在开始动画之前将设置初始化为动画的第一帧。
使用确实很简单。 首先,请考虑以下布局:
我们可以使用以下代码将高度设置为所需的高度,例如100dp
:
//convert 100dp to pixel valueint 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));
您也可以使用duration()
方法更改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); } }));
我今天偶然发现了同样的问题,我想这个问题的真正解决方案是
您将必须为该移位中涉及的所有最顶层布局设置此属性。 如果现在将一种布局的可见性设置为GONE,则另一种布局将占用该空间,因为消失的一种布局正在释放它。 将有一个默认的动画,某种程度上是“淡出”的,但是我认为您可以更改此动画-但到目前为止,我还没有测试过最后一个动画。
如果您不想一直扩展或折叠-这是一个简单的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);
对于平滑动画,请使用带有运行方法的处理程序.....并享受扩展/折叠动画
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);}
我创建了不需要指定布局高度的版本,因此它使用起来更容易,更清洁。 解决方案是在动画的第一帧中获取高度(当时至少在我的测试中可用)。 这样,您可以为视图提供任意高度和底边距。
构造函数中还有一个小技巧-底部边距设置为-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); }}
这是我的解决方案,我的ImageView
从100%
增长到200%
并使用res/anim/
文件夹中的两个动画文件恢复到其原始大小
anim_grow.xml
anim_shrink.xml
将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); } });}
我认为最简单的解决方案是将android:animateLayoutChanges="true"
为LinearLayout
,然后通过设置其可见性来显示/隐藏视图。 像吊饰一样工作,但您无法控制动画的持续时间
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();}
@Tom Esterez的 ,但已更新为根据正确使用view.measure()
// 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); }
我采用了@LenaYan的 ,该对我而言无法正常工作( 因为它在折叠和/或扩展之前将View转换为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的初始代码!
使用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();
展开/折叠视图的最佳解决方案:
@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(); }
您可以稍微扭转一下使用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())
基于@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);}
public static void slide(View v, int speed, int pos) { v.animate().setDuration(speed); v.animate().translationY(pos); v.animate().start();}// slide downslide(yourView, 250, yourViewHeight);// slide upslide(yourView, 250, 0);
/** * 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(); }
除了汤姆·埃斯特雷斯(Tom Esterez)的和埃里克·B(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);}
@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 usageexpandOrCollapseView((Your ViewGroup),(Your ViewGroup).getVisibility()!=View.VISIBLE);
这是一个正确的工作解决方案,我已经对其进行了测试:
说明:
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;}
视图v是要动画的视图,PARENT_VIEW是包含该视图的容器视图。
您走在正确的轨道上。 确保在动画开始之前将v1的布局高度设置为零。 您希望在开始动画之前将设置初始化为看起来像动画的第一帧。
好的,我刚刚找到了一个非常难看的解决方案:
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;}
随时提出更好的解决方案!
我改编 ,该方法虽然有效,但动画不连贯且不稳定。 我的解决方案基本上是用ValueAnimator
代替Animation
,后者可以与您选择的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-> h或h-> 0(h为最大高度)出发。 构造函数的三个参数是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; }}
利用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()}
是的,我同意以上意见。 实际上,似乎正确(或至少最简单的方法)是(在XML中)将初始布局高度指定为“ 0px”-然后您可以为“ toHeight”传递另一个参数(您的自定义Animation子类的构造函数的“最终高度”),例如在上面的示例中,它看起来像这样:
public DropDownAnim( View v, int toHeight ) { ... }
无论如何,希望能有所帮助! :)
我想在非常有用的添加一些内容。 如果您不知道高度,因为视图.getHeight()返回0,则可以执行以下操作来获取高度:
contentView.measure(DUMMY_HIGH_DIMENSION, DUMMY_HIGH_DIMENSION);int finalHeight = view.getMeasuredHeight();
其中DUMMY_HIGH_DIMENSIONS是您的视图的宽度/高度(以像素为单位),当使用ScrollView封装视图时,拥有一个很大的数字是合理的。
一种替代方法是使用具有以下缩放因子的缩放动画进行扩展:
ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1);
和崩溃:
ScaleAnimation anim = new ScaleAnimation(1, 1, 1, 0);
这是我用来通过动画调整视图(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;}@Overrideprotected 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();}@Overridepublic void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight);}@Overridepublic boolean willChangeBounds() { return true;}
}
转载地址:http://mgdnb.baihongyu.com/