Problem/Chalange: Mask a CPF editText field such a way, that as soon as the user starts typing digits in it, the text field inserts or updates its needed signs for the user automatically.
The first solution was very elegant and well thought and well done with a mask like ###.###.###-## but as soon as the user starts typing very fast in the text field, the mask algorithm was very slow and swallowed some digits in some cases.
For this reason, i took the chalange to myself and decided to create my own, fast cpf formatter. Bellow the solution that solved the problem and that you may use in your apps as you may need.
Solution:
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new CpfWatcher((EditText) findViewById(R.id.inputCpf)); } public class CpfWatcher implements TextWatcher { private EditText inputField; private int flag = 0; private int signsBeforeChange = 0; private int dots = 0; private int dash = 0; private String txtBeforeChange; public CpfWatcher(final EditText inputField) { this.inputField = inputField; this.inputField.setInputType(InputType.TYPE_CLASS_NUMBER); this.inputField.setHint("###.###.###-##"); this.inputField.addTextChangedListener(this); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { final String input = s.toString(); inputField.removeTextChangedListener(this); final String formattedCpf = formatCpf(input); inputField.setText(formattedCpf); inputField.addTextChangedListener(this); placeCursorDependingOnUserAction(start, formattedCpf, findOutIfUserWasTypingOrDeleting(input)); } private void placeCursorDependingOnUserAction(int start, String formattedCpf, Action action) { switch (action){ case TYPING:{ dots = formattedCpf.length() - formattedCpf.replace(".", "").length(); dash = formattedCpf.length() - formattedCpf.replace("-", "").length(); signsBeforeChange = dots+dash; inputField.setSelection(formattedCpf.length()); break; } case DELETING:{ if(start-1 < 0){ inputField.setSelection(0); }else if(txtBeforeChange.equalsIgnoreCase(formattedCpf)){ inputField.setSelection( start ); flag++; } else{ dots = formattedCpf.length() - formattedCpf.replace(".", "").length(); dash = formattedCpf.length() - formattedCpf.replace("-", "").length(); int sigsAfterChange = dots+dash; if(signsBeforeChange > sigsAfterChange){ inputField.setSelection( start-1 ); signsBeforeChange = sigsAfterChange; }else{ inputField.setSelection( start ); } } break; } } } private Action findOutIfUserWasTypingOrDeleting(String input) { Action action = Action.TYPING; if(flag <=input.length()){ flag = input.length(); action = Action.TYPING; }else{ flag = input.length(); action = Action.DELETING; } return action; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { txtBeforeChange = s.toString(); } @Override public void afterTextChanged(Editable s) { /* NOP*/ } public Editable getCpf(){ return inputField.getText(); } } public enum Action { TYPING, DELETING } private String formatCpf(String cpf) { String[] sign = new String[]{".", ".", "-"}; String[] signRegEx = new String[]{"\\.", "\\.", "-"}; int[] signPos = new int[]{4, 8, 12}; return formatCpfDependingOnLentgh(sign, signPos, removeAllSigns(cpf, signRegEx)); } private String removeAllSigns(String cpf, String[] signRegEx) { String empty = ""; String tmp = cpf; for (String sign : signRegEx) { tmp = tmp.replaceAll(sign, empty); } return tmp; } private String formatCpfDependingOnLentgh(String[] sign, int[] signPos, String cpfWithoutAnySign) { String tmp = cpfWithoutAnySign; int aLentgh = tmp.length(); if (aLentgh <= signPos[0] - 1) { // nothing to format }else if(aLentgh == signPos[0]-1) { tmp = create1Punctuation(tmp, sign); } else if (aLentgh > signPos[0] - 1 && aLentgh < signPos[1] - 1) { tmp = create2Punctuations(tmp, sign); } else if (aLentgh == signPos[1] - 1) { tmp = create3Punctuations(tmp, sign); } else if (aLentgh > signPos[1] - 1 && aLentgh < signPos[2] - 2) { tmp = create3Punctuations(tmp, sign); } else if (aLentgh == signPos[2] - 2) { tmp = create4Punctuations(tmp, sign); } else if (aLentgh > signPos[2] - 2 && aLentgh < signPos[2]) { tmp = create4Punctuations(tmp, sign); } else { tmp = trunk4Punctuations(tmp, sign); } return tmp; } private String create1Punctuation(String tmp, String[] punc) { return tmp + punc[0]; } private String create2Punctuations(String tmp, String[] punc) { String s1 = tmp.substring(0, 3); String s2 = tmp.substring(3, tmp.length()); return s1 + punc[0] + s2; } private String create3Punctuations(String tmp, String[] punc) { String s1 = tmp.substring(0, 3); String s2 = tmp.substring(3, 6); String s3 = tmp.substring(6, tmp.length()); return s1 + punc[0] + s2 + punc[1] + s3; } private String create4Punctuations(String tmp, String[] punc) { String s1 = tmp.substring(0, 3); String s2 = tmp.substring(3, 6); String s3 = tmp.substring(6, 9); String s4 = tmp.substring(9, tmp.length()); return s1 + punc[0] + s2 + punc[1] + s3 + punc[2] + s4; } private String trunk4Punctuations(String tmp, String[] punc) { String s1 = tmp.substring(0, 3); String s2 = tmp.substring(3, 6); String s3 = tmp.substring(6, 9); String s4 = tmp.substring(9, 11); return s1 + punc[0] + s2 + punc[1] + s3 + punc[2] + s4; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇Be sure to read, it will change your life!Show your work by Austin Kleon: https://amzn.to/34NVmwxThis book is a must read - it will put you in another level! (Expert)Agile Software Development, Principles, Patterns, and Practices: https://amzn.to/30WQSm2Write cleaner code and stand out!Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSvThis book is very practical, straightforward and to the point! Worth every penny!Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gpNeedless to say, these are top right?Apple AirPods Pro: https://amzn.to/2GOICxy😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆