12

I've used ObjectAnimator.ofFloat in an Android App which doesn't work on every device the same way.

MainActivity (extends Activity):

Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        startAnimation();
    }
});


public void startAnimation() {
    ImageView aniView = (ImageView) findViewById(R.id.imageView1);
    ObjectAnimator fadeOut = ObjectAnimator.ofFloat(aniView, "alpha", 0f);
    fadeOut.setDuration(2000);

    ObjectAnimator mover = ObjectAnimator.ofFloat(aniView, "translationX", -500f, 0f);
    mover.setInterpolator(new TimeInterpolator() {
        @Override
        public float getInterpolation(float input) {
            Log.v("MainActivity", "getInterpolation() " + String.format("%.4f", input));
            return input;
        }
    });
    mover.setDuration(2000);

    ObjectAnimator fadeIn = ObjectAnimator.ofFloat(aniView, "alpha", 0f, 1f);
    fadeIn.setDuration(2000);

    AnimatorSet animatorSet = new AnimatorSet();

    animatorSet.play(mover).with(fadeIn).after(fadeOut);
    animatorSet.start();
}

Samsung Galaxy S4 (Android 4.4.2):

    getInterpolation() 1,0000
    getInterpolation() 1,0000

Samsung Galaxy S5 (Android 4.4.2):

    getInterpolation() 0,0000
    getInterpolation() 0,0000
    getInterpolation() 0,0085
    getInterpolation() 0,0170
    getInterpolation() 0,0255
    ...
    ...
    getInterpolation() 0,9740
    getInterpolation() 0,9825
    getInterpolation() 0,9910
    getInterpolation() 0,9995
    getInterpolation() 1,0000

Has anyone an idea, why this doesn't work properly?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38

2 Answers2

23

On the Galaxy S4, under Developer Options, there is the option Animator duration scale. For some wild reason, this is off by default. After switching this to 1x, my animations on the S4 started to work perfectly. This may be what is causing your problem.

Mike Baxter
  • 6,868
  • 17
  • 67
  • 115
  • may be, but this shouldn't for end users – Benjamin Stürmer May 30 '15 at 17:53
  • Correct. If this was not the cause of the problem then I'm afraid I'm stumped. – Mike Baxter Jun 01 '15 at 10:01
  • This fixed it for me (although did highlight a bug in my code). Thanks for this great answer! It is worth noting that some people disable animator duration scale which can cause the same issues. – apmartin1991 Nov 13 '15 at 09:55
  • 3
    Mine is set to 1x, and I still see this issue. – AutonomousApps Aug 18 '16 at 18:57
  • @AutonomousApps, what is a device? On Samsung Galaxy S4 after turning this option to 1x an animation appeared. – CoolMind Aug 24 '16 at 08:02
  • @AutonomousApps, if an ObjectAnimator is not a requirement, I would recommend you to create animation with `AnimationUtils.loadAnimation(context, R.anim.your_xml);` or `AnimationSet`. I tested both, they work. Inside xml you can also define screen coordinates like `android:toXDelta="30%p"`. With AnimationSet you can programmatically make much. If you wish, I can write an example. – CoolMind Aug 24 '16 at 16:16
  • @AutonomousApps Unfortunately my answer only applies to the Galaxy S4. – Mike Baxter Aug 25 '16 at 09:46
  • I've encountered this on other devices as well. – Oren Mar 24 '19 at 08:59
2

Users can easily manipulate the scale value in Developer options or custom ROM providers. This could be a very tricky problem if you don't know what caused it in the first place.


Solution

You can just set an animation duration scale to 1 or any other pleasing value programmatically via Reflection API's capability. Thus, this will behave the same all across Android devices for your app. Unfortunately, Android is not giving us a warning about it on its page and a solution choice rather than Reflection due to the function itself is restricted from public usage via @hide annotation.

For more about Android's API restriction, You could read this thread;

What does @hide mean in the Android source code?

In Java

try {
    ValueAnimator.class.getMethod("setDurationScale", float.class).invoke(null, 1f);
} catch (Throwable t) {
    Log.e(TAG, t.getMessage());
}

In Kotlin

// You could also surround this line with try-catch block like above in Java example
ValueAnimator::class.java.getMethod("setDurationScale", Float::class.javaPrimitiveType).invoke(null, 1f)

I believe, this solution is more solid and robust rather than making users set it in developer settings.

Yekta Sarıoğlu
  • 1,435
  • 2
  • 12
  • 20