Android: Кастомизация TextWatcher для обработки ввода в EditText

Для работы с элементов EditText когда-нибудь вам придётся организовывать слежение за вводов пользователя. Есть стандартный OnKeyListener, но он как-то странно работает 0_о. Но обо всём попорядку.

OnKeyListener

Можно на EditText повесить OnKeyListener, который будет отслеживать нажатия на виртуальной клавиатуре. Как-то так:

((EditText)findViewById(R.id.resultNumericSystemTE)). setOnKeyListener(new View.OnKeyListener(){
   public boolean onKey(View v, int keyCode, KeyEvent event){
      return false;
      }
   }
);

Вроде всё нормально, в keyCode у нас ID нажатой клавиши. По сути всё, можем принимать нажатую кнопку или отменять ввод. Вот только это событие прокает почему-то лишь на кнопки по манипулированию: удаление символа, Enter и т.д.

TextWatcher

Ок, ищем другое решение. Находим интересный класс TextWatcher. Пробуем повесить на наш EditText:

TextWatcher inputTW = new TextWatcher() {

   public void afterTextChanged(Editable s) { 
   }

   public void beforeTextChanged(CharSequence s, int start, int count, int after){ 
            	
   }

   public void onTextChanged(CharSequence s, int start, int before, int count) {
                  
    }

};

((EditText)findViewById(R.id.MyTE)).addTextChangedListener(inputTW);

Супер! Теперь можно что-то сделать с EditText до и после нажатия. Вот только есть одна проблемка — нету ссылки на сам EditText внутри TextWatcher. Можно конечно обращаться внутри обработчика глобально, типо:

public void afterTextChanged(Editable s) { 
   EditText et = (EditText)findViewById(R.id.MyTE);
}

Да, будет всё отлично работать, но тогда при написании обработчиков на все EditText придётся дублировать кучу кода. А хотелось бы где-то прописать один раз этот, так сказать, шаблон поведения, а потом юзать его.

Свой TextWatcher

И да, можно *__* В Java есть implements — это дополнение к определению класса, реализующего некоторый интерфейс(ы). Создаём свой класс. Как пример, хотим, чтобы в EditText юзер не мог ввести больше 2 символов:

public class TextWatcherP implements TextWatcher {
	
   public EditText editText;
	
   public TextWatcherP(EditText et){
      super();
      editText = et;
   }
   public void afterTextChanged(Editable s) { 
      if(editText.getText().length() == 3){
      editText.setText(editText.getText().subSequence(0, editText.getText().length()-1));
      }
   }
   public void beforeTextChanged(CharSequence s, int start, int count, int after){ 
    	
    }
    public void onTextChanged(CharSequence s, int start, int before, int count) {
          
    }
}

По сравнению с базовым классом, наш в конструкторе принимает ссылку на EditText, что устраняет проблему описанную выше. Использовать же просто:

EditText et = (EditText)findViewById(R.id.MyTE)
TextWatcherP inputTextWatcher= new TextWatcherP(et);
et.addTextChangedListener(inputTextWatcher);