【问题标题】:How to change NumberPicker's value with animation?如何用动画改变 NumberPicker 的值?
【发布时间】:2012-11-10 03:39:13
【问题描述】:

我为 Android 创建了一个应用程序,其中有一个 NumberPicker。而且我需要更改此 NumberPicker 的值,但要使用平滑的动画,例如当您触摸它并更改其值时。

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

如果我使用以下代码:

numberPicker.setValue(5);

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

【问题讨论】:

    标签: android animation android-animation numberpicker


    【解决方案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 倍。

    如果不完全理解请再问:)

    【讨论】:

    • 哦,谢谢你非常聪明的回答 ;-) 你能解释一下你提到的第一点吗?关于dppixels。我会尝试并让您知道(当然通过将您的答案标记为已接受的答案:D)再次感谢您!
    • 我在描述中添加了它:)
    • 对我不起作用。仅使用 scrollBy(x,y) 不会产生与手动滚动相同的效果。例如。当前选择的号码根本不滚动,它只是停留在中心,然后用新号码替换。并且 UPDOWN 箭头在滚动时不会消失。
    【解决方案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 的 UPDOWN 箭头时,您可以看到此方法的实际效果。可能这不是您想要的,因为这种方法不能对动画进行太多控制,但它肯定比 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)
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2019-12-20
          • 2018-08-02
          • 1970-01-01
          • 2019-01-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-19
          • 2012-09-07
          相关资源
          最近更新 更多