2012-11-21 2 views
9

Я создал приложение для Android, в котором есть NumberPicker. И мне нужно изменить значение этого NumberPicker, но с гладкой анимацией, например, когда вы касаетесь ее и меняете ее значение.Как изменить значение NumberPicker с помощью анимации?

Например, предположим, что текущее значение равно 1, и это будет 5, я хочу, чтобы NumberPicker вращался от 1 до 2, а затем до 3 и так далее. Но я хочу, чтобы он вращался, а не сразу менял значения!

Если я использую следующий код:

numberPicker.setValue(5); 

его значение будет мгновенно изменится на 5, но я хочу, чтобы свернуть от 1 до 5, когда вы вручную потрогать и сделать его спина.

ответ

15

я решил его с помощью refelction

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

работает отлично. Я не знаю, почему этот метод является приватным.

+0

Использовал вашу технику, чтобы исправить [эту проблему] (http://stackoverflow.com/q/17708325/473232). Благодарю. – dgel

+0

Извините, что воскресил старую нить, но будет ли простой способ заставить это увеличивать NumberPicker, а не увеличиваться? Редактировать: Получил! Просто измените аргумент на false вместо true. – intA

+0

Я с гордостью могу воскресить эту старую нить. Два года спустя это работает для меня! Я начинаю задаваться вопросом, почему многие функции внутри нескольких компонентов являются частными. Это почти удручающе. –

3

Я не верю, что это поддерживается. Я думал о «грязном» способе делать это вручную:

Вы можете использовать функцию scrollBy (int x, int y) NumberPicker, называемую итеративно, чтобы сделать эффект анимации.

Некоторые вещи, чтобы принять во внимание:

  • scrollBy (х, у) работает с пикселями. Поскольку у Android есть все, что отличается от плотности экрана, то, что вы должны сделать, это сначала догадаться (я бы сделал это с помощью проб и ошибок) расстояние «dp», которое соответствует прокрутке до последовательного значения, и преобразовать это в пиксели, чтобы используй это.
  • Первый параметр scrollBy (х, у) должно принимать значение 0, в случае, если это не очевидно

Я не сделал анимации себя в Android, но есть очень хороший API, так как Honeycomb, который, вероятно, облегчает выполнение этого.

Извините, но это самый легкий, о котором я мог думать! не

EDIT: Для перехода от 'дп' в пикселях:

private float pxFromDp(float dp) 
{ 
    return dp * this.getContext().getResources().getDisplayMetrics().density; 
} 

Что вы должны сделать, это тест вызова scrollBy (0, pxFromDp (дп)) с различными значениями 'Dp', пока вы получить точный, который перемещает NumberPicker на один номер. Как только вы получите это значение, вы можете создать свой метод анимации, который при перемещении вверх по X номерам будет прокручивать X раз это расстояние dp.

Пожалуйста, переспрашивать, если вы не понимаете его полностью :)

+0

О, спасибо за ваш очень умный ответ ;-) Не могли бы вы объяснить ваш первый пункт вы упомянули? о 'dp' и' pixels'. Я попробую и дам вам знать (конечно, отметив ваш ответ как принятый ответ: D) еще раз спасибо! – Mehran

+0

Я добавил его к описанию :) – alfongj

+0

Не работает для меня. Просто использование * scrollBy (x, y) * не дает такого же эффекта, как прокрутка вручную. Например. текущий выбранный номер не прокручивается вообще, он просто остается в центре, а затем заменяется новым номером. Стрелки * UP *, * DOWN * не исчезают при прокрутке. –

1

Существует частный метод внутри NumberPicker, который

changeCurrentByOne(boolean increment) 

Вы можете опубликовать его и использовать его. Вы можете увидеть этот метод в действии, когда нажимаете UP или DOWN стрелка NumberPicker. Вероятно, это не то, что вы ищете, поскольку этот метод не дает большого контроля над анимацией, но он определенно лучше, чем setValue. Без анимации setValue выглядит ужасно для пользователя.

2

Thank @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). 
Смежные вопросы