How to animate the width and height of a Layout?

17,147

Solution 1

Sure that is possible.

Simply write your own custom-animation and modify the LayoutParams of your animated view. In this example, the animation animates the height of the animated View. Of course, animating the width is also possible.

This is how it could look like:

public class ResizeAnimation extends Animation {

    private int startHeight;
    private int deltaHeight; // distance between start and end height
    private View view;

    /**
     * constructor, do not forget to use the setParams(int, int) method before
     * starting the animation
     * @param v
     */
    public ResizeAnimation (View v) {
        this.view = v;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {

        view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
        view.requestLayout();
    }

    /**
     * set the starting and ending height for the resize animation
     * starting height is usually the views current height, the end height is the height
     * we want to reach after the animation is completed
     * @param start height in pixels
     * @param end height in pixels
     */
    public void setParams(int start, int end) {

        this.startHeight = start;
        deltaHeight = end - startHeight;
    }

    /**
     * set the duration for the hideshowanimation
     */
    @Override
    public void setDuration(long durationMillis) {
        super.setDuration(durationMillis);
    }

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

In code, create a new Animation and apply it to the RelativeLayout that you want to animate:

RelativeLayout relativeLayout = (RelativeLayout) ((LinearLayout) view.findViewById(viewId)).getParent();

// getting the layoutparams might differ in your application, it depends on the parent layout
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) relativeLayout.getLayoutParams();

ResizeAnimation a = new ResizeAnimation(relativeLayout);
a.setDuration(500);

// set the starting height (the current height) and the new height that the view should have after the animation
a.setParams(lp.height, newHeight);

relativeLayout.startAnimation(a);

Solution 2

I was looking for this for hours and Philipp Jahoda's answer was the perfect one since it can be added to an AnimationSet as well. I made a couple of small modifications to support a more similar constructor to the default animation class (except for the View parameter) and to support the "fillEnabled" property.

public class ResizeAnimation extends Animation
{

private int startWidth;
private int deltaWidth; // distance between start and end height
private int startHeight;
private int deltaHeight;

private int originalWidth;
private int originalHeight;
private View view;

private boolean fillEnabled = false;


public ResizeAnimation(View v, int startW, int endW, int startH, int endH)
{
    view = v;
    startWidth = startW;
    deltaWidth = endW - startW;

    startHeight = startH;
    deltaHeight = endH - startH;

    originalHeight = v.getHeight();
    originalWidth = v.getWidth();
}

@Override
public void setFillEnabled(boolean enabled)
{
    fillEnabled = enabled;
    super.setFillEnabled(enabled);
}


@Override
protected void applyTransformation(float interpolatedTime, Transformation t)
{
    if(interpolatedTime == 1.0 && !fillEnabled)
    {
        view.getLayoutParams().height = originalHeight;
        view.getLayoutParams().width = originalWidth;
    }
    else
    {
        if(deltaHeight != 0)
            view.getLayoutParams().height = (int) (startHeight + deltaHeight * interpolatedTime);
        if(deltaWidth != 0)
            view.getLayoutParams().width = (int) (startWidth + deltaWidth * interpolatedTime);
    }

    view.requestLayout();
}
}

Solution 3

Here's a simplified, more general (any parameter of layout), Xamarin version.

public class SizeAnimation : Animation 
{
    private int _newValue;
    private int _initialValue;
    private string _property;
    private object _target;
    private View _view;

    public SizeAnimation (View view, string property, int newValue)
    {
        _view = view;
        _property = property;
        _newValue = newValue;
        _target = view.LayoutParameters;

        // View measure is generally an enum of wrap_content / fill_parent
        // we need to make the measure explicit
        if (property == "Width" || property == "Height")
        {
            view.Measure ((int)MeasureSpecMode.Unspecified, (int)MeasureSpecMode.Unspecified);
            var unmeasuredValue = (int)view.GetType().GetProperty(property).GetValue(view);
            _initialValue = unmeasuredValue < 1
                ? (int) view.GetType().GetProperty("Measured" + _property).GetValue(view)
                : unmeasuredValue;

            view.LayoutParameters.GetType().GetProperty(property).SetValue(view.LayoutParameters,_initialValue);
        } else
        {
            _initialValue = (int) _target.GetType ().GetProperty (property).GetValue(_target);
        }
    }

    protected override void ApplyTransformation(float interpolatedTime, Transformation t) 
    {
        _target.GetType().GetProperty(_property).SetValue(_target, (int)(_initialValue + (_newValue - _initialValue) * interpolatedTime));
        _view.RequestLayout();  
    }

    public override bool WillChangeBounds() 
    {
        return true;
    }
}

Usage:

var property = "Width";
var value = 120;
var animation = new SizeAnimation(myView, property, value);
animation.Duration = 2000;
animation.Interpolator = (new LinearInterpolator());
myView.StartAnimation(animation);

"Theorically" working, not fully tested.

Solution 4

I worked with the answer by Philipp Jahoda. Here is an implementation that works for both expand and collapse animations based on the start and end values we pass. Hope it helps.

https://gist.github.com/rahulrvp/dcccd1b78cd09b31a024

Share:
17,147
Pramod Ravikant
Author by

Pramod Ravikant

Android

Updated on June 12, 2022

Comments

  • Pramod Ravikant
    Pramod Ravikant almost 2 years

    I have a LinearLayout which is expanded to full screen by hiding all other layouts and views on onClick. There is a Relativelayout above LinearLayout

    I want to apply custom animation on this. The size should should increase slowly (like in 500 milli seconds).

    But I doubt is it possible? Thanks.

    Here is what I am doing onClick:

    private void expandView (int viewId) {
        RelativeLayout relativeLayout = (RelativeLayout) ((LinearLayout) view.findViewById(viewId)).getParent();
        ViewGroup.MarginLayoutParams rlMargin = (ViewGroup.MarginLayoutParams) relativeLayout.getLayoutParams();
        rlMargin.setMargins(0, 0, 0, 0);
        relativeLayout.setLayoutParams(rlMargin);
        LinearLayout linearLayout = (LinearLayout) relativeLayout.getParent();
        hideAllLinearLayoutExcept(linearLayout.getId());
        hideAllTilesExcept(viewId);
    }
    

    viewId is the id of the LinearLayout I am clicking. This function is called from onClick()

  • Nick Mowen
    Nick Mowen over 8 years
    Just used this on a bottom sheet in one of my apps, the only answer that actually worked! Thank you very much!
  • ravindu1024
    ravindu1024 over 7 years
    This is perfect. Thank you! Btw, I added an answer with a slight modification to your code to make it more generic.