FoamyGuy
FoamyGuy

Reputation: 46856

Scale animation with small "rebound"

I am attempting to use XML Animations to scale a view up from 0% size to 150% size (50% bigger than "real size"). Then scale back down from 150% size to "real size" of 100%. The intention is a bit of a "rebound" effect where the view will grow up from nothing to bigger than it's supposed to be and then "rebound" or "snap back" to the proper size.

Here is an image that illustrates of timeline of what I would like to accomplish.

Timeline of desired Animations

I am using this XML animation code:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <scale android:fromXScale="0"
        android:fromYScale="0"
        android:toXScale="1.5"
        android:toYScale="1.5"
        android:duration="200"
        android:pivotX="50%"
        android:pivotY="100%"
        android:fillAfter="false"
        />
    <scale android:fromXScale="1.5"
        android:fromYScale="1.5"
        android:toXScale="1.0"
        android:toYScale="1.0"
        android:duration="100"
        android:pivotX="50%"
        android:pivotY="100%"
        android:startOffset="200"
        />
</set>

The problem that I am running into is that after the first scale animation completes the view is correctly at 150% size, but when it runs the second animation it is scaling relative to the size that we finished the first animation at (150%) as the "real size" 100%, or 1.0.

So when the second animation runs instead of my view scaling back down from 150% to the real size of 100% it is actually scaling from 150% of 150% (225%) back down to 150%. Which means that at the end of the animation the view is still too large, sized at 150% of the real size in my case.

Is there some way to instruct the second Animation or the Animation set to ignore the ending value from the first animation and instead just use the "real" size of the view so that the code would be more literally interpreted and would scale from 150% of real size down to 100% of real size.

I have tried every combination of fillBefore, fillAfter, and fillEnabled that I can think of on each scale animation and on the set itself, but thus far I've been unable to find a combination of those settings that will make it behave how I intend for it to.

I found This Article which seems like it may be relevant to my situation, and this led me to test out some more combinations of the various fill properties. But so far I'm still not able to get the outcome I am hoping for.

Upvotes: 1

Views: 852

Answers (2)

Finn Marquardt
Finn Marquardt

Reputation: 597

shouldnt a simple Interpolator for the animation work, I mean you do not have as much fine control over the animation with the exact timings but something like this should do a similar trick without that much work:

<scale android:fromXScale="0"
    android:fromYScale="0"
    android:toXScale="1.0"
    android:toYScale="1.0"
    android:duration="300"
    android:pivotX="50%"
    android:pivotY="100%"
    android:fillAfter="true"
    android:interpolator="@android:anim/overshoot_interpolator"
    />

Upvotes: 2

RonTLV
RonTLV

Reputation: 2576

Facebook has a java library called Rebound that models spring dynamics and adds real world physics. Example code:

import com.facebook.rebound.BaseSpringSystem;
import com.facebook.rebound.SimpleSpringListener;
import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringSystem;
import com.facebook.rebound.SpringUtil;

/**
 * This Activity presents an ImageView that scales down when pressed and returns to full size when
 * released. This demonstrates a very simple integrates a very Simple integration of a Rebound
 * Spring model to drive a bouncy animation as the photo scales up and down. You can control the
 * Spring configuration by tapping on the blue nub at the bottom of the screen to reveal the
 * SpringConfiguratorView. From this view you can adjust the tension and friction of the animation
 * spring and observe the effect these values have on the animation.
 */
public class MainActivity extends Activity {

  private final BaseSpringSystem mSpringSystem = SpringSystem.create();
  private final ExampleSpringListener mSpringListener = new ExampleSpringListener();
  private FrameLayout mRootView;
  private Spring mScaleSpring;
  private View mImageView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);
    mRootView = (FrameLayout) findViewById(R.id.root_view);
    mImageView = mRootView.findViewById(R.id.image_view);

    // Create the animation spring.
    mScaleSpring = mSpringSystem.createSpring();

    // Add an OnTouchListener to the root view.
    mRootView.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
          case MotionEvent.ACTION_DOWN:
            // When pressed start solving the spring to 1.
            mScaleSpring.setEndValue(1);
            break;
          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_CANCEL:
            // When released start solving the spring to 0.
            mScaleSpring.setEndValue(0);
            break;
        }
        return true;
      }
    });
  }

  @Override
  public void onResume() {
    super.onResume();
    // Add a listener to the spring when the Activity resumes.
    mScaleSpring.addListener(mSpringListener);
  }

  @Override
  public void onPause() {
    super.onPause();
    // Remove the listener to the spring when the Activity pauses.
    mScaleSpring.removeListener(mSpringListener);
  }

  private class ExampleSpringListener extends SimpleSpringListener {
    @Override
    public void onSpringUpdate(Spring spring) {
      // On each update of the spring value, we adjust the scale of the image view to match the
      // springs new value. We use the SpringUtil linear interpolation function mapValueFromRangeToRange
      // to translate the spring's 0 to 1 scale to a 100% to 50% scale range and apply that to the View
      // with setScaleX/Y. Note that rendering is an implementation detail of the application and not
      // Rebound itself. If you need Gingerbread compatibility consider using NineOldAndroids to update
      // your view properties in a backwards compatible manner.
      float mappedValue = (float) SpringUtil.mapValueFromRangeToRange(spring.getCurrentValue(), 0, 1, 1, 0.5);
      mImageView.setScaleX(mappedValue);
      mImageView.setScaleY(mappedValue);
    }
  }

}

Upvotes: 2

Related Questions