Android NumberPicker с Formatter не форматируется при первом рендеринге

У меня есть NumberPicker, у которого есть форматировщик, который форматирует отображаемые числа либо при вращении NumberPicker, либо при вводе значения вручную. Это работает отлично, но когда сначала отображается NumberPicker, и я инициализирую его с помощью setValue(0), 0 не форматируется (он должен отображаться как "-" вместо 0). Как только я вращаю NumberPicker с этого момента, все работает.

Как я могу принудительно форматировать NumberPicker - как при первом рендеринге, так и при вводе числа вручную с клавиатуры?

Это мой форматировщик

public class PickerFormatter implements Formatter {

 private String mSingle;
 private String mMultiple;

 public PickerFormatter(String single, String multiple) {
    mSingle = single;
    mMultiple = multiple;
 }

 @Override
 public String format(int num) {
    if (num == 0) {
        return "-";
    }
    if (num == 1) {
        return num + " " + mSingle;
    }
    return num + " " + mMultiple;
 }

}

Я добавляю свой форматтер к сборщику с помощью setFormatter(), это все, что я делаю для сборщика.

    picker.setMaxValue(max);
    picker.setMinValue(min);
    picker.setFormatter(new PickerFormatter(single, multiple));
    picker.setWrapSelectorWheel(wrap);

Ответ 1

Я также столкнулся с этой досадной маленькой ошибкой . Использовал технику из этого ответа, чтобы придумать неприятное, но эффективное исправление.

NumberPicker picker = (NumberPicker)view.findViewById(id.picker);
picker.setMinValue(1);
picker.setMaxValue(5);
picker.setWrapSelectorWheel(false);
picker.setFormatter(new NumberPicker.Formatter() {
    @Override
    public String format(int value) {
        return my_formatter(value);
    }
});

try {
    Method method = picker.getClass().getDeclaredMethod("changeValueByOne", boolean.class);
    method.setAccessible(true);
    method.invoke(picker, true);
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalArgumentException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

Вызов этого частного метода changeValueByOne сразу после создания экземпляра моего набора чисел, похоже, приводит к форматированию, достаточному для того, чтобы вести себя так, как нужно. Выбор номера подходит и чист, при этом первое значение отформатировано правильно. Как я сказал, противный, но эффективный.

Ответ 2

dgel solution не работает для меня: когда я нажимаю на сборщик, форматирование снова исчезает. Эта ошибка вызвана установкой входного фильтра на EditText внутри NumberPicker, когда setDisplayValues не используется. Поэтому я придумал это решение:

Field f = NumberPicker.class.getDeclaredField("mInputText");
f.setAccessible(true);
EditText inputText = (EditText)f.get(mPicker);
inputText.setFilters(new InputFilter[0]);

Ответ 3

У меня была та же проблема, и вместо этого я использовал метод setDisplayedValues().

int max = 99;
String[] values = new String[99];
values[0] = "-" + mSingle
values[1] = 
for(int i=2; i<=max; i++){
    makeNames[i] = String.valueOf(i) + mMultiple;
}
picker.setMinValue(0);
picker.setMaxValue(max);
picker.setDisplayedValues(values)

Это не позволяет пользователю устанавливать значение вручную в сборщике.

Ответ 4

Вызов частного метода changeValueByOne() через отражение, как описано в более раннем ответе, работает для меня на API Level 16 (Android 4.1.2 и выше), но, похоже, он не помогает на API Level 15 (Android 4.0.3), однако!

Что работает для меня на уровне API 15 (и выше) - это использовать собственный пользовательский форматтер для создания массива String и передать его с помощью метода setDisplayedValues() в подборщик чисел.

Смотрите также: Android 3.x и 4.x NumberPicker Example

Ответ 5

Мне удалось исправить это, позвонив

picker.invalidate();

сразу после установки форматирования.

Ответ 6

Это решение разработало для меня API 18-26 без использования отражения и без использования setDisplayedValues().

Он состоит из двух шагов:

  • Убедитесь, что первый элемент отображается, установив его видимость невидимым (я использовал "Макет инспектор", чтобы увидеть разницу, когда он показывает, он не логичен, но View.INVISIBLE действительно делает вид видимым).

    private void initNumberPicker() {
     // Inflate or create your BugFixNumberPicker class
     // Do your initialization on bugFixNumberPicker...
    
     bugFixNumberPicker.setFormatter(new NumberPicker.Formatter() {
        @Override
        public String format(final int value) {
            // Format to your needs
            return aFormatMethod(value);
        }
     });
    
     // Fix for bug in Android Picker where the first element is not shown
     View firstItem = bugFixNumberPicker.getChildAt(0);
      if (firstItem != null) {
        firstItem.setVisibility(View.INVISIBLE);
      }
    }
    
  • Подкласс NumberPicker и убедитесь, что никакие события кликов не пройдут, поэтому сбой, при котором элементы-подборщики исчезают при касании, не может произойти.

    public class BugFixNumberPicker extends NumberPicker {
    
     public BugFixNumberPicker(Context context) {
        super(context);
     }
    
     public BugFixNumberPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
     }
    
     public BugFixNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
     }
    
     @Override
     public boolean performClick() {
        return false;
     }
    
     @Override
     public boolean performLongClick() {
        return false;
     }
    
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
        return false;
     }
    }