Friday, 27 July 2012

Android - how to create a sliding View

Creating a View which slides back and forth between an original position and a displacement position is easy post- Ice Cream Sandwich (by means of the new ObjectAnimator class and setTranslationX(float translationX) etc methods). Doing this pre- Ice Cream Sandwich is not so easy. Below is a class I created which you can use as a basis for creating a View which toggles (slides) between an original position and a displacement position. The way it works is to extend FrameLayout (could be a different View depending on your need), to start the translate right/left animations when the toggleSlide() method is called, and to listen for the end of the animations in order to relocate itself right/left as appropriate, as follows:

public class SlidingFrameLayout extends FrameLayout
{
  private final int durationMilliseconds = 1000;
  private final int displacementPixels = 200;

  private boolean isInOriginalPosition = true;
  private boolean isSliding = false;

  public SlidingFrameLayout(Context context)
  {
    super(context);
  }

  public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs);
  }

  public SlidingFrameLayout(Context context, AttributeSet attrs, int defStyle)
  {
    super(context, attrs, defStyle);
  }

  @Override
  protected void onAnimationEnd()
  {
    super.onAnimationEnd();

    if (isInOriginalPosition)
      offsetLeftAndRight(displacementPixels);
    else
      offsetLeftAndRight(-displacementPixels);

    isSliding = false;
    isInOriginalPosition = !isInOriginalPosition;
  }

  @Override
  protected void onLayout(
      boolean changed,
      int left,
      int top,
      int right,
      int bottom)
  {
    super.onLayout(changed, left, top, right, bottom);

    // need this since otherwise this View jumps back to its original position
    // ignoring its displacement
    // when (re-)doing layout, e.g. when a fragment transaction is committed
    if (changed && !isInOriginalPosition)
      offsetLeftAndRight(displacementPixels);
  }

  public void toggleSlide()
  {
    // check whether frame layout is already sliding
    if (isSliding)
      return; // ignore request to slide

    if (isInOriginalPosition)
      startAnimation(new SlideRightAnimation());
    else
      startAnimation(new SlideLeftAnimation());

    isSliding = true;
  }

  private class SlideRightAnimation extends TranslateAnimation
  {
    public SlideRightAnimation()
    {
      super(
        Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, displacementPixels,
        Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 0);

      setDuration(durationMilliseconds);
      setFillAfter(false);
    }
  }

  private class SlideLeftAnimation extends TranslateAnimation
  {
    public SlideLeftAnimation()
    {
      super(
        Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, -displacementPixels,
        Animation.ABSOLUTE, 0,
        Animation.ABSOLUTE, 0);

      setDuration(durationMilliseconds);
      setFillAfter(false);
    }
  }
}

2 comments:

Anonymous said...

Hi Adil,

Sergio here.
To use the new com.andriod.animation API, available from Honeycomb in older API levels, you can use the library 9oldandroids:

http://nineoldandroids.com/

adil said...

Hey Sergio,

Thanks for the comment :) As discussed offline, the 'nineoldandroids' library is handy for simplifying animation coding but has some limitations in that Views translated horizontally/vertically appear as moved but are still clickable in their original (pre-translate) locations.