如何用动画改变 NumberPicker 的值?
Posted
技术标签:
【中文标题】如何用动画改变 NumberPicker 的值?【英文标题】:How to change NumberPicker's value with animation? 【发布时间】:2012-11-10 03:39:13 【问题描述】:我为 android 创建了一个应用程序,其中有一个 NumberPicker。而且我需要更改此 NumberPicker 的值,但要使用平滑的动画,例如当您触摸它并更改其值时。
例如,假设当前值为 1,它将为 5,我希望 NumberPicker 从 1 旋转到 2,然后旋转到 3,依此类推。但我希望它旋转而不仅仅是立即改变值!
如果我使用以下代码:
numberPicker.setValue(5);
它的值会立即变为 5,但我希望它从 1 滚动到 5,就像您手动触摸它并使其旋转时一样。
【问题讨论】:
【参考方案1】:我通过反射解决了它
/**
* 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();
完美运行。我不知道为什么这个方法是私有的
【讨论】:
用你的技术修复了this issue。谢谢。 很抱歉复活了一个旧线程,但是有没有一种简单的方法可以让 NumberPicker 递减而不是递增?编辑:明白了!只需将参数更改为 false 而不是 true。 这为我吐出了 NoSuchMethodException。我正在使用一个扩展 NumberPicker 的类。 @DaleJulian 有同样的问题,如果你有一个扩展 NumberPicker 的类,你应该把方法放在你的类中并调用:getClass().getSuperclass().getDeclaredMethod("changeValueByOne", boolean.class); 在私有黑名单 API 上使用反射的方法在启动 API 28+ 时不再起作用,请参阅 developer.android.com/distribute/best-practices/develop/…【参考方案2】:我不相信这是本机支持的。我想到了一种手动操作的“混乱”方式:
您可以使用 NumberPicker 的 scrollBy(int x, int y) 函数迭代调用来制作动画效果。
需要考虑的一些事项:
scrollBy(x,y) 适用于像素。由于 Android 具有所有不同的屏幕密度,您应该首先猜测(我会通过反复试验来完成)对应于滚动到连续值的“dp”距离,并将其转换为像素以便使用它。 scrollBy(x,y)的第一个参数应该取0,以防不明显我自己没有在 Android 中制作动画,但自 Honeycomb 以来有一个非常好的 API,它可能很容易实现。
对不起,这是我能想到的最简单的方法!
编辑:从“dp”转换为像素:
private float pxFromDp(float dp)
return dp * this.getContext().getResources().getDisplayMetrics().density;
您需要做的是使用不同的 'dp' 值测试调用 scrollBy(0, pxFromDp(dp)) ,直到获得将 NumberPicker 向上移动一个数字的确切值。获得该值后,您可以创建动画方法,当向上移动 X 个数字时,该动画方法将滚动该 dp 距离的 X 倍。
如果不完全理解请再问:)
【讨论】:
哦,谢谢你非常聪明的回答 ;-) 你能解释一下你提到的第一点吗?关于dp
和pixels
。我会尝试并让您知道(当然通过将您的答案标记为已接受的答案:D)再次感谢您!
我在描述中添加了它:)
对我不起作用。仅使用 scrollBy(x,y) 不会产生与手动滚动相同的效果。例如。当前选择的号码根本不滚动,它只是停留在中心,然后用新号码替换。并且 UP、DOWN 箭头在滚动时不会消失。【参考方案3】:
感谢@passsy。启发了他的回答:此解决方案支持多个递增/递减步骤。此外,可以在 开始延迟 和 多个 numberPickers(无需额外编码) 的情况下执行。
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.
用法:
// 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 (e.g. 100 milliseconds).
【讨论】:
【参考方案4】:NumberPicker里面有一个私有方法,就是
changeCurrentByOne(boolean increment)
您可以将其公开并使用。当按下 NumberPicker 的 UP 或 DOWN 箭头时,您可以看到此方法的实际效果。可能这不是您想要的,因为这种方法不能对动画进行太多控制,但它肯定比 setValue 好。没有任何动画 setValue 对用户来说看起来很糟糕。
【讨论】:
【参考方案5】:感谢@passy 的回答。可以使用 Kotlin 扩展函数进一步简化:
fun NumberPicker.animateChange(isIncrement: Boolean)
Handler().post
try
javaClass.getDeclaredMethod("changeValueByOne", Boolean::class.javaPrimitiveType).also function ->
function.isAccessible = true
function.invoke(this, isIncrement)
catch (e: Exception)
Log.e(javaClass.simpleName, e.message)
【讨论】:
以上是关于如何用动画改变 NumberPicker 的值?的主要内容,如果未能解决你的问题,请参考以下文章