Intereting Posts

Android EditText, проблема с клавиатурой textWatcher

Я работаю над Android-приложением, и у меня есть EditText, где пользователь может вводить числа. Я хочу отформатировать номер, используя разные форматы валют (например, ##, ##, ###), и я хочу сделать это «на лету», т.е. когда пользователь вводит каждую цифру (а не при нажатии на ввод). Я googled вокруг, и наткнулся TextWatcher, который я впервые нашел многообещающим, но это оказалось абсолютной болью. Я отлаживаю свой код на телефоне HTC Desire, который имеет только мягкую клавиатуру.

Теперь я хочу получить обратный вызов, когда пользователь нажимает цифры (от 0 до 9), del (backspace) и вводит ключ. Из моего тестирования я нашел их (по крайней мере, на моем телефоне)

1) editText onKeyListener вызывается, когда пользователь нажимает del или вводит ключ. Когда пользователь нажимает enter, функция onKey вызывается дважды для одного ввода (что, я считаю, для ACTION_UP и ACTION_DOWN). Когда пользователь нажимает del, onKey вызывается один раз (только для ACTION_DOWN), который я не знаю, почему. OnKey никогда не вызывается, когда пользователь нажимает любые цифры (от 0 до 9), которые я тоже не могу понять.

2) Функции обратного вызова TextWatchers 3 вызываются (beforeTextChanged, onTextChanged, afterTextChanged) всякий раз, когда пользователь нажимает любое число (от 0 до 9). Поэтому я подумал, что с помощью TextWatcher и onKeyListener я могу получить все обратные вызовы, которые мне нужны.

Теперь мои вопросы таковы.

1) Сначала на моей мягкой клавиатуре HTC есть клавиша (символ клавиатуры со стрелкой вниз), и когда я нажимаю на нее, клавиатура уходит в отставку без какого-либо обратного вызова. Я все еще не могу поверить, что андроид позволяет пользователю редактировать поле и уходить в отставку, не позволяя программе обрабатывать (сохранять) редактирование. Теперь мой editText показывает одно значение, и мой объект имеет другое значение (я сохраняю редактирование пользователя при вводе и обратное нажатие на клавиатуре путем переименования значения editText со значением в объекте, но у меня нет ответа на эту клавиатуру вниз) ,

2) Во-вторых, я хочу отформатировать номер после ввода пользователем новой цифры. Скажем, у меня уже есть 123 на editText, а введенный пользователем нажат 4, я хочу, чтобы мой editText отображал 1,234. Я получаю полный номер onTextChanged () и afterTextChanged (), и я могу отформатировать номер и вернуть его в editText в любом из этих обратных вызовов. Какой из них я должен использовать? Какая практика?

3) Третий – самая важная проблема. Когда приложение запускается, я помещаю текущее значение объекта в editText. Скажем, я положил 123 на onResume (), и когда пользователь вводил цифру (скажем, 4), я хочу, чтобы она была 1234. Но в моем обратном вызове onTextChanged я получаю 4123. Когда я нажимаю еще один ключ (скажем, 5), я Получив 45123. Таким образом, для пользовательских входов курсор editText указывает на конец текста. Но когда значение задается вручную, курсор editText не обновляется. Я считаю, что мне нужно что-то сделать в callback-ответах textWatcher, но я не знаю, что я должен делать.

Я отправляю свой код ниже.

public class AppHome extends AppBaseActivity { private EditText ed = null; private NumberFormat amountFormatter = null; private boolean isUserInput = true; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.app_home_screen); ed = (EditText)findViewById(R.id.main_amount_textfield); amountFormatter = new DecimalFormat("##,##,###"); ed.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_DOWN) return true; String strippedAmount = ed.getText().toString().replace(",", ""); if(keyCode == KeyEvent.KEYCODE_DEL){ //delete pressed, strip number of comas and then delete least significant digit. strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1); int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } myObject.amount = amountNumeral; isUserInput = false; setFormattedAmount(amountNumeral,ed.getId()); }else if(keyCode == KeyEvent.KEYCODE_ENTER){ //enter pressed, save edits and resign keyboard int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } myObject.amount = amountNumeral; isUserInput = false; setFormattedAmount(myObject.amount,ed.getId()); //save edits save(); //resign keyboard.. InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS); } return true; } }); TextWatcher inputTextWatcher = new TextWatcher() { public void afterTextChanged(Editable s) { if(isUserInput == false){ //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:( isUserInput = true; return; } String strippedAmount = ed.getText().toString().replace(",", ""); int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } isUserInput = false; setFormattedAmount(amountNumeral,ed.getId()); } public void beforeTextChanged(CharSequence s, int start, int count, int after){ } public void onTextChanged(CharSequence s, int start, int before, int count) { } }; ed.addTextChangedListener(inputTextWatcher); }//end of onCreate... public void setFormattedAmount(Integer amount, Integer inputBoxId){ double amountValue = 0; String textString =null; TextView amountInputBox = (TextView) findViewById(inputBoxId); amountValue = Double.parseDouble(Integer.toString(amount)); textString = amountFormatter.format(amountValue).toString(); amountInputBox.setText(textString); } } 

Я знаю, что это большой вопрос, но я работаю над этой же проблемой в течение 2 дней. Я новичок в android и все еще не могу поверить, что нет простого способа обрабатывать данные TextEdit на лету (я делал то же самое на iphone с легкостью). Спасибо всем

EDIT : после использования входного фильтра

 InputFilter filter = new InputFilter() { public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { String strippedAmount = dest.toString() + source; strippedAmount = strippedAmount.replace(",", ""); int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } return amountFormatter.format(amountNumeral).toString(); } }; ed.setFilters(new InputFilter[]{filter}); 

Когда приложение запускается, я помещаю 1,234 в editText

 myObject.amount = 1234; ed.setText(amountFormatter.format(myObject.amount).toString()); 

Затем, когда пользователь нажимает на editText, появляется клавиатура и говорит, что пользователь вводит цифру 6

Я получаю: 61234 Я хочу: 12346

Solutions Collecting From Web of "Android EditText, проблема с клавиатурой textWatcher"

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

  TextWatcher inputTextWatcher = new TextWatcher() { public void afterTextChanged(Editable s) { if(isUserInput == false){ //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:( isUserInput = true; ed.setSelection(ed.getText().length()); return; } String strippedAmount = ed.getText().toString().replace(",", ""); int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } isUserInput = false; setFormattedAmount(amountNumeral,ed.getId()); } public void beforeTextChanged(CharSequence s, int start, int count, int after){ } public void onTextChanged(CharSequence s, int start, int before, int count) { } }; ed.addTextChangedListener(inputTextWatcher); ed.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int length = ed.getText().length(); ed.setCursorVisible(true); ed.setSelection(length); } }); ed.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_UP) return true; String strippedAmount = ed.getText().toString().replace(",", ""); if(keyCode == KeyEvent.KEYCODE_DEL){ //delete pressed, strip number of comas and then delete least significant digit. strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1); int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } isUserInput = false; setFormattedAmount(amountNumeral,ed.getId()); return true; }else if(keyCode == KeyEvent.KEYCODE_ENTER){ //enter pressed, save edits and resign keyboard int amountNumeral = 0; try{ amountNumeral = Integer.parseInt(strippedAmount); } catch(NumberFormatException e){ } isUserInput = false; setFormattedAmount(amountNumeral,ed.getId()); //save edits //resign keyboard.. InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS); return true; } return false; } }); 

То, что я сделал, это onClick () из editText, я принудительно помещаю курсор в конец текущего текста EditText, и я сделал то же самое, когда пользователь нажал любую цифру. Надеюсь, это помогает кому-то … Спасибо за каждого, кто пытался помочь.

Для входа Masked вы можете подкласса InputFilter

Ниже приведен пример подкласса InputFilter, который использует все буквы в нижнем регистре:

  /** * This filter will capitalize all the lower case letters that are added * through edits. */ public static class AllCaps implements InputFilter { public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { for (int i = start; i < end; i++) { if (Character.isLowerCase(source.charAt(i))) { char[] v = new char[end - start]; TextUtils.getChars(source, start, end, v, 0); String s = new String(v).toUpperCase(); if (source instanceof Spanned) { SpannableString sp = new SpannableString(s); TextUtils.copySpansFrom((Spanned) source, start, end, null, sp, 0); return sp; } else { return s; } } } return null; // keep original } } 

Вышеприведенный код взят из реализации Android InputFilter

После нескольких часов работы я сделал маска для ввода телефона. Для начала после ввода «123456» он преобразует его в «+1 (234) 56». После удаления любого символа из любой позиции курсор перемещается в правильное положение, а не в начало или конец.

В работе:

  etPhone.addTextChangedListener(new PhoneWatcher(etPhone)); 

В классе:

 private class PhoneWatcher implements TextWatcher { private static final String PHONE_MASK = "+# (###) ###-##-##"; private final char[] PHONE_MASK_ARRAY = PHONE_MASK.toCharArray(); private boolean isInTextChanged; private boolean isInAfterTextChanged; private EditText editText; private int shiftCursor; private String text; private int cursor; public PhoneWatcher(EditText editText) { super(); this.editText = editText; isInTextChanged = false; isInAfterTextChanged = false; } @Override public synchronized void beforeTextChanged(CharSequence s, int start, int count, int after) { shiftCursor = after - count; } @Override public synchronized void onTextChanged(CharSequence s, int start, int before, int count) { if (!isInTextChanged) { isInTextChanged = true; StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char symbol = s.charAt(i); if (symbol >= '0' && symbol <= '9') sb.append(symbol); } String digits = sb.toString(); sb.setLength(0); int j = 0; for (int i = 0; i < digits.length(); i++) { char digit = digits.charAt(i); while (j < PHONE_MASK_ARRAY.length) { if (PHONE_MASK_ARRAY[j] == '#') { sb.append(digit); j++; break; } else { sb.append(PHONE_MASK_ARRAY[j]); j++; } } } cursor = editText.getSelectionStart(); text = sb.toString(); if (shiftCursor > 0) { if (cursor > text.length()) cursor = text.length(); else { while (cursor < PHONE_MASK_ARRAY.length && PHONE_MASK_ARRAY[cursor - 1] != '#') { cursor++; } } } else if (shiftCursor < 0) { while (cursor > 0 && PHONE_MASK_ARRAY[cursor - 1] != '#') { cursor--; } } } } public synchronized void afterTextChanged(Editable s) { if (!isInAfterTextChanged) { isInAfterTextChanged = true; editText.setText(text); editText.setSelection(cursor); isInTextChanged = false; isInAfterTextChanged = false; } } }