如何用动画改变NumberPicker的值?

问题描述 投票:0回答:6

我创建了一个Android应用程序,其中有一个NumberPicker。我需要更改此 NumberPicker 的值,但要具有平滑的动画,就像您触摸它并更改其值时一样。

例如,假设当前值为 1,它将是 5,我希望 NumberPicker 从 1 旋转到 2,然后旋转到 3,依此类推。但我希望它旋转而不仅仅是立即改变值!

如果我使用以下代码:

numberPicker.setValue(5);

它的值会立即变为 5,但我希望它从 1 滚动到 5,就像你手动触摸它并使其旋转一样。

android animation android-animation numberpicker
6个回答
17
投票

我通过反思解决了这个问题

        /**
         * 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();
            }
        }

工作完美。我不知道为什么这个方法是私有的


3
投票

我不相信这是原生支持的。我想到了一种手动完成的“混乱”方式:

可以使用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 个数字时,将滚动 X 倍此 dp 距离。

如果不完全明白请再问:)


3
投票

感谢@passsy。启发了他的回答:该解决方案支持多个递增/递减步骤。此外,可以通过启动延迟多个数字选择器(无需额外编码)来执行。

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).

1
投票

NumberPicker 内部有一个私有方法,即

changeCurrentByOne(boolean increment)

您可以公开并使用它。当按下 NumberPicker 的 UPDOWN 箭头时,您可以看到此方法的作用。可能这不是您想要的,因为这个方法没有提供太多的动画控制,但它绝对比 setValue 更好。没有任何动画setValue对用户来说看起来很糟糕。


1
投票

感谢@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)
        }
    }
}

0
投票

您可以通过伪造键盘事件来增加或减少而不进行反射:

numberPicker.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, increment ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP));
© www.soinside.com 2019 - 2024. All rights reserved.