21

At the first click of a button, I want to slide a View along the x-axis (by 200 pixels to the right let's say). And on the second press of the button, I want to slide the View back along the x-axis to its original position.

The View.setTranslationX(float) method jumps the View to the target horizontal locations with calls myView.setTranslationX(200); and myView.setTranslationX(0); respectively. Anyone know how I can slide the View to the target horizontal locations instead?

Note 1: A TranslateAnimation is no good since it doesn't actually move the View but only presents the illusion of it moving.

Note 2: I didn't realise at the time of posing the question that the setTranslationX(float) and setTranslationY(float) methods were introduced as of API Level 11. If you're targeting Honeycomb (API Level 11) upwards, then Gautam K's answer will suffice. Otherwise, see my answer for a suggested solution.

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147

3 Answers3

26

Try looking into ObjectAnimator and its super class Value Animator

you can do something like this ObjectAnimator anim = ObjectAnimator.ofFloat(this, "translationX", 0,200); and then anim.start();

Use a boolean value and toggle it with 200,0 in the object animator to slide back

PS: you can use setDuration method to set how long the animation should take to complete

Edit :

Try looking at the support library which provides backward compatibility.

Edit

As @AdilHussain pointed out there is a library called nineoldandroids which can be used for the same purpose on older androids.

Gautam
  • 7,868
  • 12
  • 64
  • 105
  • @AdilHussain: then you have to implement the functionality with threads yourself on phones which don't support it. Just check if the phone supports it or else write some simple fallback . That is the best option. – Gautam Jun 25 '12 at 13:18
  • 1
    Found this brilliant library which does the job: [http://nineoldandroids.com](http://nineoldandroids.com). Library available for download as a `jar` from here: [https://github.com/JakeWharton/NineOldAndroids/downloads](https://github.com/JakeWharton/NineOldAndroids/downloads) – Adil Hussain Jun 25 '12 at 14:55
  • 1
    Looks like I spoke too soon. When doing a translation using the [nineoldandroids](https://github.com/JakeWharton/NineOldAndroids) library on older phones (pre-Honeycomb), the buttons within the translated view appear to have moved but are still clickable in their old locations (and not their new locations). I've emailed the author of the library but haven't received a response yet. – Adil Hussain Jun 28 '12 at 10:44
  • I am guessing nineoldandroids is open source , so Just dive into the source code , and try to fix the bug. – Gautam Jun 29 '12 at 01:05
5

This one's had me stumped for a fair few days (getting it to work pre- Ice Cream Sandwich) but I think I've finally got there! (thanks to Gautam K and Mike Israel for the leads) What I did in the end was to extend my View (a FrameLayout) to start the translate right/left animations as required and to listen for the end of the animations in order to relocate my FrameLayout 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)
  {
    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);
    }
  }
}

And, lastly, to slide the SlidingFrameLayout right/left, all you've got to do is call the SlidingFrameLayout.toggleSlide() method. Of course you can tweak this SlidingFrameLayout for your purposes to slide a greater number of pixels, to slide for longer etc, but this should be sufficient to get you started :)

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147
2

I had a similar issue, TranslateAnimation does actually move the view if you call setFillAfter now (android bug). I had to do something similar so I said, hey lets use an animation listener and then just move everything to the correct location. Unfortunately there is a bug on animation listeners as well (stackoverflow solution). So I created my own layout class according to the solution in the stackoverflow solution and I was good to go :)

Community
  • 1
  • 1
MikeIsrael
  • 2,871
  • 2
  • 22
  • 34
  • Thanks for the response and the links. Goes to show that using `TranslateAnimation` for this is a bit messy. When I said `TranslateAnimation `does not actually move the View (despite using `setFillAfter(true)`), I meant that the clickable items are still in their original positions. See this video for an example of what I mean: [http://www.youtube.com/watch?v=VY3kMFrA3wU](http://www.youtube.com/watch?v=VY3kMFrA3wU) – Adil Hussain Jun 25 '12 at 12:49
  • 1
    yeah that looks exactly like the bug with fillAfter. Weird though that the first button does work, what is the difference between the two buttons, any? – MikeIsrael Jun 25 '12 at 12:54
  • 1
    The solution I recommended should still work for all versions. I built a custom layout class which Overrides onAnimationEnd() and there I change the marginleft of the object as needed to move it to the correct spot. – MikeIsrael Jun 25 '12 at 13:01
  • 1
    The difference is that the pre-Honeycomb (pre- API 11) animations (of the `android.view.animation` package) change the visual appearance of a `View` but don't actually change the `View` itself, so that buttons in the `View` for example _appear_ in a new place but are still actually where they were before the animation was run. This is not a bug apparently. Just the way animations were pre-Honeycomb. The new animations classes set out to address this and other shortcomings. – Adil Hussain Jun 25 '12 at 15:03
  • @AdilHussain that is exactly what my answer addresses. After the animation finishes you can manually move the views to their appropriate location. It will work on pre and post honeycomb. – MikeIsrael Jun 25 '12 at 15:07
  • Hi Mike, I tried your suggestion because the `nineoldandroids` library has some shortcomings but I'm finding that when I set my View's margins programmatically (by getting my View's layout params, setting the new margins on the layout params, and then setting the modified layout params to be my View's layout params), it causes my View (and the children within it) to resize to fit within its parent. I need to shift my View to the right/left however without resizing. Any suggestions? Or could you perhaps edit your answer to include some sample code of how you did it? – Adil Hussain Jul 02 '12 at 17:54
  • 1
    @AdilHussain I am guessing that you are trying to have some items off screen a bit right? If so I had a similar issue and found a fairly simple solution. I would just resize the parent view to be larger than the whole screen. Is that what you were looking for? – MikeIsrael Jul 03 '12 at 06:12
  • Ah, I see what you mean: to put my `View` in a `HorizontalScrollView`, to set the width of the `HorizontalScrollView` to accommodate my `View` + displacement and then to slide my `View` (by playing with the `View`'s left margin) within the `HorizontalScrollView`. Haven't tried it but I'm guessing it would be just a small tweak to the solution I'm about to add to this thread... – Adil Hussain Jul 03 '12 at 10:51
  • @AdilHussain you could use a scroll view and manually scroll it, I just used a subclassed LinearLayout (in order to grab the end animtaiton event) and would just move it left be setting margin to -30dp for example. Scroll view may be a bit of a cleaner solution and avoiding the negative margins. – MikeIsrael Jul 03 '12 at 11:00
  • @AdilHussain were you able to get it working with the horizontal scrollview? – MikeIsrael Jul 03 '12 at 16:05
  • Didn't try it in the end :( Stuck with the solution I had as it is independent of the parent View that my sliding View is contained in. It's holding out ok so far so going to leave it be for now and press on. Already spent more time on it than expected. Thanks for the help btw. Appreciate it :) – Adil Hussain Jul 03 '12 at 16:22