Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Sunday, April 24, 2016

Android: Expand/collapse animation

Android: Expand/collapse animation


Let's say I have a vertical linearLayout with :

[v1]  [v2]  

By default v1 has visibily = GONE. I would like to show v1 with an expand animation and push down v2 at the same time.

I tried something like this:

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

But with this solution, I have a blink when the animation starts. I think it's caused by v1 displaying full size before the animation is applied.

With javascript, this is one line of jQuery! Any simple way to do this with android?

Answer by Micah Hainline for Android: Expand/collapse animation


You are on the right track. Make sure you have v1 set to have a layout height of zero right before the animation starts. You want to initialize your setup to look like the first frame of the animation before starting the animation.

Answer by Tom Esterez for Android: Expand/collapse animation


Ok, I just found a VERY ugly solution :

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

Feel free to propose a better solution !

Answer by Seth Nelson for Android: Expand/collapse animation


I was trying to do what I believe was a very similar animation and found an elegant solution. This code assumes that you are always going from 0->h or h->0 (h being the maximum height). The three constructor parameters are view = the view to be animated (in my case, a webview), targetHeight = the maximum height of the view, and down = a boolean which specifies the direction (true = expanding, 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;      }  }  

Answer by Daniel Kopyc for Android: Expand/collapse animation


Yes, I agreed with the above comments. And indeed, it does seem like the right (or at least the easiest?) thing to do is to specify (in XML) an initial layout height of "0px" -- and then you can pass in another argument for "toHeight" (i.e. the "final height") to the constructor of your custom Animation sub-class, e.g. in the example above, it would look something like so:

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

Anyways, hope that helps! :)

Answer by gardarh for Android: Expand/collapse animation


I would like to add something to the very helpful answer above. If you don't know the height you'll end up with since your views .getHeight() returns 0 you can do the following to get the height:

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

Where DUMMY_HIGH_DIMENSIONS is the width/height (in pixels) your view is constrained to ... having this a huge number is reasonable when the view is encapsulated with a ScrollView.

Answer by ChristophK for Android: Expand/collapse animation


An alternative is to use a scale animation with the following scaling factors for expanding:

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

and for collapsing:

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

Answer by user783873 for Android: Expand/collapse animation


This is a snippet that I used to resize the width of a view (LinearLayout) with animation.

The code is supposed to do expand or shrink according the target size. If you want a fill_parent width, you will have to pass the parent .getMeasuredWidth as target width while setting the flag to true.

Hope it helps some of you.

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;  }    @Override  protected 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();  }    @Override  public void initialize(int width, int height, int parentWidth, int parentHeight) {      super.initialize(width, height, parentWidth, parentHeight);  }    @Override  public boolean willChangeBounds() {      return true;  }  

}

Answer by Tom Esterez for Android: Expand/collapse animation


I see that this question became popular so I post my actual solution. The main advantage is that you don't have to know the expanded height to apply the animation and once the view is expanded, it adapts height if content changes. It works great for me.

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

Answer by Kaloyan Donev for Android: Expand/collapse animation


Here is my solution. I think it is simpler. It only expands the view but can easy be extended.

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

Answer by NhamPD for Android: Expand/collapse animation


Make sure you have v1 set to have a layout height of zero right before the animation starts. You want to initialize your setup to look like the first frame of the animation before starting the animation.

Answer by Phil for Android: Expand/collapse animation


This is really simple with droidQuery. For starts, consider this layout:

                                                          

We can animate the height to the desired value - say 100dp - using the following code:

//convert 100dp to pixel value  int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());  

Then use droidQuery to animate. The simplest way is with this:

$.animate("{ height: " + height + "}", new AnimationOptions());  

To make the animation more appealing, consider adding an easing:

$.animate("{ height: " + height + "}", new AnimationOptions().easing($.Easing.BOUNCE));  

You can also change the duration on AnimationOptions using the duration() method, or handle what happens when the animation ends. For a complex example, try:

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

Answer by Mr.Fu for Android: Expand/collapse animation


I stumbled over the same problem today and I guess the real solution to this question is this

  

You will have to set this property for all topmost layouts, which are involved in the shift. If you now set the visibility of one layout to GONE, the other will take the space as the disappearing one is releasing it. There will be a default animation which is some kind of "fading out", but I think you can change this - but the last one I have not tested, for now.

Answer by Nir Hartmann for Android: Expand/collapse animation


If you don't want to expand or collapse all the way - here is a simple 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; } }

Usage:

HeightAnimation heightAnim = new HeightAnimation(view, view.getHeight(), viewPager.getHeight() - otherView.getHeight());  heightAnim.setDuration(1000);  view.startAnimation(heightAnim);  

Answer by user834799 for Android: Expand/collapse animation


For Smooth animation please use Handler with run method.....And Enjoy Expand /Collapse animation

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

}

And Call using this code:

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

Other solution is:

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

Answer by Michał K for Android: Expand/collapse animation


I created version in which you don't need to specify layout height, hence it's a lot easier and cleaner to use. The solution is to get the height in the first frame of the animation (it's available at that moment, at least during my tests). This way you can provide a View with an arbitrary height and bottom margin.

There's also one little hack in the constructor - the bottom margin is set to -10000 so that the view stays hidden before the transformation (prevents flicker).

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

Answer by Elenasys for Android: Expand/collapse animation


This was my solution, my ImageView grows from 100% to 200% and return to his original size, using two animation files inside res/anim/ folder

anim_grow.xml

         

anim_shrink.xml

         

Send an ImageView to my method setAnimationGrowShrink()

ImageView img1 = (ImageView)findViewById(R.id.image1);  setAnimationGrowShrink(img1);  

setAnimationGrowShrink() method:

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

Answer by Jacek Kwiecień for Android: Expand/collapse animation


I think the easiest solution is to set android:animateLayoutChanges="true" to your LinearLayout and then just show/hide view by seting its visibility. Works like a charm, but you have no controll on the animation duration

Answer by LenaYan for Android: Expand/collapse animation


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

Answer by Erik B for Android: Expand/collapse animation


@Tom Esterez's answer, but updated to use view.measure() properly per Android getMeasuredHeight returns wrong values !

    // http://easings.net/      // https://gist.github.com/ebabel/8ff41cad01e9ce1dd9ce      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);      }  

Answer by Geraldo Neto for Android: Expand/collapse animation


I took @LenaYan 's solution that didn't work properly to me (because it was transforming the View to a 0 height view before collapsing and/or expanding) and made some changes.

Now it work's great, by taking the View's previous height and start expanding with this size. Collapsing is the same.

You can simply copy and paste the code below:

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

Usage:

//Expanding the View     expand(yourView, 2000, 200);    // Collapsing the View          collapse(yourView, 2000, 100);  

Easy enough!

Thanks LenaYan for the initial code!

Related Posts:

0 comments:

Post a Comment

Popular Posts

Fun Page

Powered by Blogger.