Last active
September 22, 2016 20:51
-
-
Save starkej2/55558cb9a0eea543404677fcc0d05be4 to your computer and use it in GitHub Desktop.
A robust TextWatcher that groups input into the specified group sizes, with separators between them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Formats the watched EditText to groups of characters, with separators between them. | |
*/ | |
public class GroupedInputFormatWatcher implements TextWatcher { | |
private char separator; | |
private String separatorString; | |
private int groupSize; | |
/** | |
* Breakdown of this regex: | |
* ^ - Start of the string | |
* (\\d{4}\\s)* - A group of four digits, followed by a whitespace, e.g. "1234 ". Zero or more | |
* times. | |
* \\d{0,4} - Up to four (optional) digits. | |
* (?<!\\s)$ - End of the string, but NOT with a whitespace just before it. | |
* <p> | |
* Example of matching strings: | |
* - "2304 52" | |
* - "2304" | |
* - "" | |
*/ | |
private final String regex = "^(\\d{4}\\s)*\\d{0,4}(?<!\\s)$"; | |
private boolean isUpdating = false; | |
private final EditText editText; | |
public GroupedInputFormatWatcher(@NonNull EditText editText) { | |
this(editText, 4, ' '); | |
} | |
public GroupedInputFormatWatcher(@NonNull EditText editText, int groupSize, char separator) { | |
this.editText = editText; | |
this.groupSize = groupSize; | |
this.separator = separator; | |
this.separatorString = String.valueOf(separator); | |
} | |
@Override | |
public void onTextChanged(CharSequence s, int start, int before, int count) { | |
} | |
@Override | |
public void beforeTextChanged(CharSequence s, int start, int count, int after) { | |
} | |
@Override | |
public void afterTextChanged(Editable s) { | |
String originalString = s.toString(); | |
// Check if we are already updating, to avoid infinite loop. | |
// Also check if the string is already in a valid format. | |
if (isUpdating || originalString.matches(regex)) { | |
return; | |
} | |
// Set flag to indicate that we are updating the Editable. | |
isUpdating = true; | |
// First all whitespaces must be removed. Find the index of all whitespace. | |
LinkedList<Integer> spaceIndices = new LinkedList<Integer>(); | |
for (int index = originalString.indexOf(separator); index >= 0; index = originalString.indexOf(separator, index + 1)) { | |
spaceIndices.offerLast(index); | |
} | |
// Delete the whitespace, starting from the end of the string and working towards the beginning. | |
Integer spaceIndex = null; | |
while (!spaceIndices.isEmpty()) { | |
spaceIndex = spaceIndices.removeLast(); | |
s.delete(spaceIndex, spaceIndex + 1); | |
} | |
// Loop through the string again and add whitespaces in the correct positions | |
for (int i = 0; ((i + 1) * groupSize + i) < s.length(); i++) { | |
s.insert((i + 1) * groupSize + i, separatorString); | |
} | |
// Finally check that the cursor is not placed before a whitespace. | |
// This will happen if, for example, the user deleted the digit '5' in | |
// the string: "1234 567". | |
// If it is, move it back one step; otherwise it will be impossible to delete | |
// further numbers. | |
int cursorPos = editText.getSelectionStart(); | |
if (cursorPos > 0 && s.charAt(cursorPos - 1) == separator) { | |
editText.setSelection(cursorPos - 1); | |
} | |
isUpdating = false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Adapted from http://stackoverflow.com/a/29774102/1169961