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);

person A. Steenbergen    schedule 17.07.2013    source источник
comment
Не могли бы вы опубликовать код, это поможет ответить.   -  person Ankit Aggarwal    schedule 17.07.2013
comment
это просто стандартная реализация, на самом деле:   -  person A. Steenbergen    schedule 18.07.2013
comment
добавлен некоторый код, но код не проблема, средство выбора номеров просто не выполняет код при первом рендеринге   -  person A. Steenbergen    schedule 18.07.2013
comment
я немного новичок в этом, но можете ли вы вызвать format() из функции конструктора? Я не думаю, что это называется изначально. Это поможет?   -  person Ankit Aggarwal    schedule 18.07.2013
comment
Я только что погуглил и нашел этот отчет о проблеме в проекте Android: code.google.com/p/android/issues/detail?id=35482 Подождем исправления.   -  person georgepiva    schedule 04.09.2013


Ответы (10)


Версия Kotlin на основе ответа Николая

private fun initNumberPicker() {
    nrPicker.children.iterator().forEach {
        if (it is EditText) it.filters = arrayOfNulls(0)    // remove default input filter
    }
}
person Emanuel Moecklin    schedule 06.01.2021

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

Field f = NumberPicker.class.getDeclaredField("mInputText");
f.setAccessible(true);
EditText inputText = (EditText)f.get(mPicker);
inputText.setFilters(new InputFilter[0]);
person torvin    schedule 07.11.2014
comment
У меня работало на API 25. Спасибо! - person rjr-apps; 22.12.2017

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

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 сразу после создания экземпляра моего средства выбора чисел, похоже, приводит к тому, что модуль форматирования ведет себя так, как должен. Средство выбора чисел выглядит красиво и чисто, первое значение отформатировано правильно. Как я уже сказал, неприятно, но эффективно.

person dgel    schedule 30.09.2013
comment
Почему-то в программировании для Android вам приходится очень часто использовать неприятные хаки. - person A. Steenbergen; 12.10.2013
comment
не могу поверить, что это еще не исправлено. - person Sebastian Hojas; 16.03.2014
comment
об этой ошибке сообщили 2 года назад... ты издеваешься, гугл? все еще проблема на андроиде 4.4.2 - person serine; 04.06.2014
comment
У меня не работает: всякий раз, когда я нажимаю на средство выбора, форматирование снова исчезает. Я придумал другое решение. - person torvin; 07.11.2014
comment
Действительно - это до сих пор присутствует в Marshmallow :( - person Nathan Osman; 13.10.2015
comment
Все еще не исправлено, но метод changeValueByOne() отсутствует в более новых SDK, поэтому точное решение больше не работает. :-/ - person osxdirk; 12.01.2016
comment
Сегодня, 11 ноября 2016 г. Проблема все еще существует :-( - person Maveňツ; 01.11.2016
comment
Приятно, когда хорошие ребята избавляют тебя от таких проблем =) - person RexSplode; 06.03.2017
comment
Android 7.1.1 и этот баг до сих пор кажется совершенно незамеченным. - person Andrew No; 20.04.2017
comment
Илон Маск запустил Теслу в космос, и эта проблема существует до сих пор. - person Karl-John Chow; 26.04.2018

У меня была та же проблема, и вместо этого я использовал метод 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)

Однако это не позволяет пользователю устанавливать значение вручную в средстве выбора.

person Lumbi    schedule 04.03.2014
comment
Из справочника для разработчиков Android: Примечание. Длина отображаемых значений массив, установленный через setDisplayedValues(String[]), должен быть равен диапазону выбираемых чисел, который равен getMaxValue() - getMinValue() + 1. - person MikeJfromVA; 05.04.2018

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

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

  1. Убедитесь, что первый элемент отображается, установив его видимость на невидимый (я использовал Layout Inspector, чтобы увидеть разницу с тем, когда он отображается, это не логично, но 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);
      }
    }
    
  2. Подкласс 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;
     }
    }
    
person Sebastian    schedule 06.07.2017

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

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

См. также: Android 3.x и 4. x Пример средства выбора номера

person HeNRGi    schedule 11.12.2013

Ответ, предоставленный NoActivity, сработал для меня, но мне нужно было только сделать:

View firstItem = bugFixNumberPicker.getChildAt(0);
if (firstItem != null) {
  firstItem.setVisibility(View.INVISIBLE);
}

чтобы решить проблему. Мне не нужно было создавать подкласс NumberPicker. Я не видел проблемы, когда элементы выбора исчезали при касании.

person Christopher Chua    schedule 29.05.2018
comment
Я могу подтвердить, что отформатированные элементы исчезают при касании (проверено на Lollipop). - person Nikolai; 08.01.2019

Вот мое решение, основанное на ответах torvin и Себастьян. Вам не нужно ничего подклассировать или использовать отражение.

View editView = numberPicker.getChildAt(0);

if (editView instanceof EditText) {
    // Remove default input filter
    ((EditText) editView).setFilters(new InputFilter[0]);
}
person Nikolai    schedule 07.01.2019

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

picker.invalidate();

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

person user1504495    schedule 04.11.2014

Улучшен ответ Николая, если выбранный индекс не равен 0. Не очень подходит для выступлений, но решает проблему.

for(index in numberPicker.minValue..numberPicker.maxValue) {

        val editView = numberPicker.getChildAt(index-numberPicker.minValue)
        if (editView != null && editView is EditText) {
            // Remove default input filter
            (editView as EditText).filters = arrayOfNulls(0)
        }
    }
person Đorđe Trbović    schedule 25.10.2019