Last active
May 31, 2020 08:12
-
-
Save nasserkhosravi/a5c1c5421bde542e57271565c9941c6d to your computer and use it in GitHub Desktop.
//- Add separator to text each 3 number //- Handle selection when selection position is after separator
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
package com.nasserkhosravi.uikit | |
import android.text.Editable | |
import android.text.SpannableStringBuilder | |
import android.text.TextWatcher | |
import android.widget.EditText | |
import android.widget.TextView | |
/** | |
* Your [EditText] should have ltr layout direction | |
*/ | |
class NumberTextWatcher( | |
private val edView: EditText, | |
private val listener: ((EditText, CharSequence?) -> Unit)? | |
) : TextWatcher { | |
private var beforeText: String = "" | |
override fun afterTextChanged(s: Editable?) { | |
listener?.invoke(edView, s.toString()) | |
} | |
/* | |
* start: is the start index of the red highlighted text (that is about to be deleted) | |
* count: is the length of the red highlighted text (that is about to be deleted) | |
* after: is the length of the green highlighted text (that is about to be added) | |
* */ | |
override fun beforeTextChanged(p0: CharSequence?, start: Int, count: Int, after: Int) { | |
beforeText = p0.toString() | |
} | |
/* | |
* start: is the start index of the green highlighted text (that just got added). | |
* This is the same as the start of beforeTextChanged. | |
* before: is the length of the red highlighted text (that just got deleted). | |
* This is the same as the count of beforeTextChanged. | |
* count: is the length of the green highlighted text (that just got added). | |
* This is the same as the after of beforeTextChanged. | |
* */ | |
override fun onTextChanged(p0: CharSequence?, start: Int, before: Int, count: Int) { | |
if (p0.isNullOrEmpty()) { | |
return | |
} | |
// 1. get cursor position : p0 = start + before | |
val initialCursorPosition = start + before | |
val targetString = if (isSeparatorRemoved(p0)) { | |
StringBuilder(p0).deleteCharAt(initialCursorPosition - 2).toString() | |
} else { | |
p0 | |
} | |
//2. get digit count after cursor position : c0 | |
val numOfDigitsToRightOfCursor = getNumberOfDigits( | |
beforeText.substring( | |
initialCursorPosition, | |
beforeText.length | |
) | |
) | |
val newAmount = formatAmount(targetString.toString()) | |
edView.removeTextChangedListener(this) | |
if (newAmount.toString() == "0") | |
edView.setText("") | |
else { | |
edView.setText(newAmount, TextView.BufferType.SPANNABLE) | |
//set new cursor position | |
edView.setSelection(getNewCursorPosition(numOfDigitsToRightOfCursor, newAmount.toString())) | |
} | |
edView.addTextChangedListener(this) | |
} | |
private fun isSeparatorRemoved(p0: CharSequence): Boolean { | |
val beforeDigit = getNumberOfDigits(beforeText) | |
val newDigit = getNumberOfDigits(p0.toString()) | |
return newDigit == beforeDigit | |
} | |
private fun formatAmount(amount: String): SpannableStringBuilder { | |
val sNonNumeric = removeNonNumeric(amount) | |
val result = sNonNumeric.formatEachBySeparator(3, ",") | |
return SpannableStringBuilder(result) | |
} | |
private fun removeNonNumeric(numberString: String): String { | |
val strings = StringBuilder() | |
for (i in numberString) { | |
if (i.isDigit()) { | |
strings.append(i) | |
} | |
} | |
return strings.toString() | |
} | |
private fun getNewCursorPosition(digitCountToRightOfCursor: Int, numberString: String): Int { | |
var position = 0 | |
var c = digitCountToRightOfCursor | |
for (i in numberString.reversed()) { | |
if (c == 0) | |
break | |
if (i.isDigit()) | |
c-- | |
position++ | |
} | |
return numberString.length - position | |
} | |
private fun getNumberOfDigits(text: String): Int { | |
var count = 0 | |
for (i in text) | |
if (i.isDigit()) | |
count++ | |
return count | |
} | |
private fun String.formatEachBySeparator(each: Int, separator: String): String { | |
val target = this | |
val result = StringBuilder() | |
for (index in target.indices) { | |
if (index % each == 0) { | |
if (index > 0) { | |
result.append(separator) | |
} | |
} | |
result.append(target[index]) | |
} | |
return result.toString() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment