EditText не обновляется после изменения текста в TextWatcher

У меня есть EditText и TextWatcher.

Скелет моего кода:

EditText x;
x.addTextChangedListener(new XyzTextWatcher());

XyzTextWatcher implements TextWatcher() {
    public synchronized void afterTextChanged(Editable text) {
        formatText(text);
    }
}

Мой метод formatText() вставляет дефисы в некоторые позиции текста.

private void formatText(Editable text) {
    removeSeparators(text);

    if (text.length() >= 3) {
        text.insert(3, "-");
    }
    if (text.length() >= 7) {
        text.insert(7, "-");
    }
}

private void removeSeparators(Editable text) {
    int p = 0;
    while (p < text.length()) {
        if (text.charAt(p) == '-') {
            text.delete(p, p + 1);
        } else {
            p++;
        }
    }
}

У меня проблема: то, что отображается в моем EditText, не синхронизировано с редактируемым. Когда я отлаживал код, я увидел, что текст переменной (редактируемый) имеет ожидаемое значение, но то, что показано в EditText, не всегда соответствует редактируемому.

Например, когда у меня есть текст x = "123-456-789", я вырезаю текст "456" из x вручную. После форматирования мой редактируемый объект имеет значение «123-789-». Однако значение, отображаемое в моем EditText, равно «123--789».

Однако в большинстве случаев они имеют одинаковое значение.

Я предположил, что EditText ЯВЛЯЕТСЯ редактируемым, и они всегда должны совпадать. Я что-то упускаю?


person Karthz    schedule 15.09.2011    source источник
comment
Можете ли вы показать код, в котором вы меняете текст в EditText/Editable?   -  person slayton    schedule 15.09.2011
comment
Код в комментариях плохо читается. Не могли бы вы добавить этот код в свой исходный пост в блоке кода, чтобы я мог его прочитать?   -  person slayton    schedule 15.09.2011
comment
@slayton Прости за это! Я добавил код в свой исходный пост сейчас. Спасибо.   -  person Karthz    schedule 15.09.2011


Ответы (1)


Хорошо, вы никогда не меняете EditText только редактируемый. Android EditTexts не являются дочерними элементами класса Editable. Строки являются подклассами класса Editable. OnTextChangedListener не получает EditText в качестве аргумента, а Editable/String отображается в EditText. После того, как вы отформатируете Editable с помощью дефисов, вам нужно будет обновить EditText. Что-то вроде этого должно работать нормально:

class MyClass extends Activity{

    //I've ommited the onStart(), onPause(), onStop() etc.. methods

    EditText x;
    x.addTextChangedListener(new XyzTextWatcher());

    XyzTextWatcher implements TextWatcher() {
        public synchronized void afterTextChanged(Editable text) {
            String s = formatText(text);
            MyClass.this.x.setText(s);
        }
    }

}

Чтобы предотвратить замедление, почему бы не изменить метод formatText примерно так?

private Editable formatText(Editable text) {
    int sep1Loc = 3;
    int sep2Loc = 7;

    if(text.length==sep1Loc)
    text.append('-');

    if(text.length==sep2Loc)
    text.append('-');

    return text;
}

Примечание. Я не проверял это

person slayton    schedule 15.09.2011
comment
Спасибо. Я тоже попробовал это, и это дало мне ожидаемый результат. Однако это значительно замедлило ввод. Мне пришлось немного подождать, чтобы ввести каждую цифру. Итак, я подумал, что идея была неправильной. - person Karthz; 15.09.2011
comment
Это неудивительно, потому что каждый раз, когда вы вводите новую цифру, вызывается слушатель. Вам действительно не нужно переформатировать строку каждый раз, когда вводится цифра, только когда строка достигает определенной длины. Попробуйте поместить предложение if в начало метода formatText(), который немедленно возвращает строку, слишком короткую или нет не той длины. - person slayton; 15.09.2011
comment
Большое спасибо. Я попробую это и посмотрю, сделает ли это быстрее. - person Karthz; 15.09.2011
comment
Нет, это не поможет в моем случае, так как при вставке числа в EditText не будут вставлены дефисы. afterTextChanged() будет вызываться только один раз для вставки. Не для каждой цифры. Спасибо. - person Karthz; 16.09.2011
comment
Хорошо, это не было задумано как идеальная замена, а как пример более простой реализации. - person slayton; 16.09.2011
comment
Да, я понимаю. :) Спасибо за все быстрые ответы. Теперь это работает очень хорошо. - person Karthz; 16.09.2011
comment
Как вы заставили это работать без переполнения стека из-за повторного вызова afterTextChanged? - person chucky; 20.12.2011
comment
@chucky, наверное, лучше всего, если вы просто зададите новый вопрос со своим кодом. Вы получите больше ответов. - person slayton; 20.12.2011
comment
вам не нужно удалять и повторно добавлять textWatcher в методе afterTextChanged? Я думаю, что это приведет к переполнению стека, как сказал @chucky. - person loeschg; 01.11.2013
comment
На самом деле вам не нужно обновлять EditText, вам просто нужно отредактировать Editable! Если ваши изменения не приняты, попробуйте проверить InputFilters, установленные в этом Editable. Фильтры могут игнорировать ваши изменения. Означает Editable.get/setFilters. - person demaksee; 01.09.2016
comment
И еще идея: использовать какой-нибудь логический флаг, чтобы предотвратить рекурсивный вызов afterTextChanged. Есть такой ответ: stackoverflow.com/a/28865522/755313 - person demaksee; 01.09.2016
comment
Использование EditText.setText будет иметь побочный эффект, например удаление заглавных букв. Есть ли способ предотвратить это? - person Gyome; 18.05.2017