10

NumberPickerがあるAndroid用のアプリケーションを作成しました。そして、このNumberPickerの値を変更する必要がありますが、タッチして値を変更するときのようなスムーズなアニメーションを使用します。

たとえば、現在の値が1で、5になると仮定すると、NumberPickerを1から2に、次に3に回転させる必要があります。しかし、値を即座に変更するだけでなく、回転させたいのです。

次のコードを使用する場合:

numberPicker.setValue(5);

その値はすぐに5に変わりますが、手動でタッチして回転させるときのように、1から5にロールアップしたいと思います。

4

5 に答える 5

16

反射で解決しました

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

完璧に動作します。このメソッドがプライベートである理由がわかりません

于 2013-03-18T11:23:26.547 に答える
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;
}

あなたがしなければならないことは、NumberPickerを1つ上に動かす正確な値が得られるまで、異なる'dp'値でscrollBy(0、pxFromDp(dp))を呼び出すことをテストすることです。その値を取得したら、Xの数値を上に移動すると、このdp距離のX倍にスクロールするアニメーションメソッドを作成できます。

完全に理解していない場合は、もう一度質問してください:)

于 2012-11-21T20:19:38.710 に答える
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).
于 2017-02-11T13:46:18.717 に答える
1

NumberPicker内にプライベートメソッドがあります。

changeCurrentByOne(boolean increment)

公開して使用することができます。NumberPickerの上矢印または下矢印を押すこのメソッドの動作を確認できます。このメソッドはアニメーションをあまり制御できないため、おそらくそれはあなたが探しているものではありませんが、setValueよりも間違いなく優れています。アニメーションがないと、 setValueはユーザーにとってひどいものに見えます。

于 2013-01-07T20:21:16.483 に答える
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)
        }
    }
}
于 2019-05-18T17:09:36.510 に答える