如何用动画改变 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 倍。

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

【讨论】:

哦,谢谢你非常聪明的回答 ;-) 你能解释一下你提到的第一点吗?关于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)
        
    

【讨论】:

以上是关于如何用动画改变 NumberPicker 的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何用动画改变按钮的位置

如何用android动画移动/改变对象的方向?

如何用javascript制作动画

如何用补间动画效果绘制三个 js 线条几何?

如何用jquery动态改变输入框的readonly属性

如何用OpenGL实现计算机图形学中的平移动画