How to change NumberPicker value using animation?

I created an Android app that has NumberPicker in it. And I need to change the value of this NumberPicker, but with smooth animation, for example, when you touch it and change its value.

For example, suppose the current value is 1, and it will be 5, I want NumberPicker to rotate from 1 to 2, and then to 3 and so on. But I want him to rotate not only instantly change values!

If I use the following code:

numberPicker.setValue(5); 

its value will instantly change to 5, but I want it to curl from 1 to 5, as when manually touching and twisting.

+9
android animation numberpicker android-animation
Nov 21 '12 at 19:40
source share
5 answers

I solved it using refelction

  /** * using reflection to change the value because * changeValueByOne is a private function and setValue * doesn't call the onValueChange listener. * * @param higherPicker * the higher picker * @param increment * the increment */ private void changeValueByOne(final NumberPicker higherPicker, final boolean increment) { Method method; try { // refelction call for // higherPicker.changeValueByOne(true); method = higherPicker.getClass().getDeclaredMethod("changeValueByOne", boolean.class); method.setAccessible(true); method.invoke(higherPicker, increment); } catch (final NoSuchMethodException e) { e.printStackTrace(); } catch (final IllegalArgumentException e) { e.printStackTrace(); } catch (final IllegalAccessException e) { e.printStackTrace(); } catch (final InvocationTargetException e) { e.printStackTrace(); } } 

works great. I do not know why this method is private

+15
Mar 18 '13 at 11:23
source share

I do not believe this is supported. I was thinking of a dirty way to do this manually:

You can use the NumberPicker scrollBy (int x, int y) function, called iteratively, to make the animation effect.

Some things to consider:

  • scrollBy (x, y) works with pixels. Since Android has everything that differs from the screen density, what you have to do is first guess (I would do it with trial and error) the "dp" distance, which corresponds to scrolling to a sequential value, and convert it to pixels to use it.
  • The first parameter, scrollBy (x, y), should be set to 0, if this is not obvious.

I didn’t do the animations myself on Android, but there is a very good API since Honeycomb, which probably makes it easier to do this.

Sorry, but this is the easiest I could think of!

EDIT: to convert from 'dp' to pixels:

 private float pxFromDp(float dp) { return dp * this.getContext().getResources().getDisplayMetrics().density; } 

What you will need to do is call scrollBy (0, pxFromDp (dp)) with different "dp" values ​​until you get the exact number that moves NumberPicker one number. Once you get this value, you can create your own animation method, which when scrolling up through X numbers will scroll X times this distance dp.

Please ask again if you do not understand it completely :)

+3
Nov 21 '12 at 20:19
source share

Thanks @passsy. Inspired my answer: this solution supports several increment / decrement steps . In addition, it can be executed with a start delay for several numberPickers (without additional encoding) .

 class IncreaseValue { private int counter = 0; private final NumberPicker picker; private final int incrementValue; private final boolean increment; private final Handler handler = new Handler(); private final Runnable fire = new Runnable() { @Override public void run() { fire(); } }; /*public*/ IncreaseValue(final NumberPicker picker, final int incrementValue) { this.picker = picker; if (incrementValue > 0) { increment = true; this.incrementValue = incrementValue; } else { increment = false; this.incrementValue = -incrementValue; } } /*public*/ void run(final int startDelay) { handler.postDelayed(fire, startDelay); // This will execute the runnable passed to it (fire) // after [startDelay in milliseconds], ASYNCHRONOUSLY. } private void fire() { ++counter; if (counter > incrementValue) return; try { // refelction call for // picker.changeValueByOne(true); final Method method = picker.getClass().getDeclaredMethod("changeValueByOne", boolean.class); method.setAccessible(true); method.invoke(picker, increment); } catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) { Log.d(TAG, "...", e); } handler.postDelayed(fire, 120); // This will execute the runnable passed to it (fire) // after 120 milliseconds, ASYNCHRONOUSLY. Customize this value if necessary. } } 

Using:

 // Suppose picker1 as a NumberPicker IncreaseValue increasePicker1 = new IncreaseValue(picker1, 10); // So picker1 will be increased 10 steps. increasePicker1.run(0); // Increase immediately. If you want to run it from onCreate(), onStart() or ... of your activity, you will probably need to pass a positive value to it (eg 100 milliseconds). 
+3
Feb 11 '17 at 13:46 on
source share

There is a private method inside NumberPicker that

 changeCurrentByOne(boolean increment) 

You can make it public and use it. You can see this method in action when you press the UP or DOWN arrow for NumberPicker. This is probably not what you are looking for, since this method does not give much control over the animation, but it is definitely better than setValue. Without any animation, setValue looks awful for the user.

+1
Jan 07 '13 at 20:21
source share

Thanks @passy for the answer. May be further simplified with the Kotlin Extension function:

 fun NumberPicker.animateChange(isIncrement: Boolean) { Handler().post { try { javaClass.getDeclaredMethod("changeValueByOne", Boolean::class.javaPrimitiveType).apply { isAccessible = true invoke(this, isIncrement) } } catch (e: Exception) { Log.e(javaClass.simpleName, e.message) } } } 
0
May 18 '19 at 17:09
source share



All Articles