Skip to content

Instantly share code, notes, and snippets.

@behdad222
Last active March 19, 2024 23:19
Show Gist options
  • Save behdad222/60b4f99ceff14167b1299cb387ef4b20 to your computer and use it in GitHub Desktop.
Save behdad222/60b4f99ceff14167b1299cb387ef4b20 to your computer and use it in GitHub Desktop.
class PriceTextInputLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.textInputStyle
) : TextInputLayout(context, attrs, defStyleAttr) {
val locale = Locale(LOCALE_LANGUAGE_FA)
override fun onAttachedToWindow() {
super.onAttachedToWindow()
editText?.apply {
doAfterTextChanged { editable ->
if (editable == null) {
return@doAfterTextChanged
}
val content = editable.toString().getNumber()
val formattedPersian = String.format(locale, "%d", content)
if (formattedPersian != editable.toString()) {
var selectionPosition = selectionStart.orZero()
setText(formattedPersian)
if (selectionPosition == 0 || selectionPosition > text.length) {
selectionPosition = text.length.orZero()
}
setSelection(selectionPosition)
return@doAfterTextChanged
}
val separatorWidth = paint.measureText(SEPARATED_CHAR.toString()).toInt().orZero()
formatThousandsSeparator(editable, separatorWidth)
}
}
}
private fun formatThousandsSeparator(editable: Editable, separatorWidth: Int) {
val textLength = editable.length
// first remove any previous span
val spans = editable.getSpans(
0, editable.length,
SeparatorSpan::class.java
)
for (i in spans.indices) {
editable.removeSpan(spans[i])
}
for (i in textLength - 1 downTo 0) {
val marginSPan = SeparatorSpan(separatorWidth)
editable.setSpan(marginSPan, i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
class SeparatorSpan(private val separatorWidth: Int) : ReplacementSpan() {
override fun getSize(
paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?
): Int {
val widths = FloatArray(end - start)
paint.getTextWidths(text, start, end, widths)
var sum = 0
for (i in widths.indices) {
sum += widths[i].toInt()
}
val formattedText = makeFormattedText(text)
val newStart = getNewStart(start, formattedText)
if (formattedText[newStart] == SEPARATED_CHAR) {
sum += separatorWidth
}
return sum
}
override fun draw(
canvas: Canvas,
text: CharSequence,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
val formattedText = makeFormattedText(text)
val newStart = getNewStart(start, formattedText)
var numberChar = 1
if (formattedText[newStart] == SEPARATED_CHAR) {
numberChar++
}
canvas.drawText(
formattedText,
newStart, newStart + numberChar,
x, y.toFloat(),
paint
)
}
private fun makeFormattedText(text: CharSequence): String {
val formattedText = StringBuilder(text)
var thoseGroupIndex = 0
for (i in text.length - 1 downTo 0) {
thoseGroupIndex++
if (thoseGroupIndex % 3 == 0 && i != 0) {
formattedText.insert(i, SEPARATED_CHAR)
}
}
return formattedText.toString()
}
private fun getNewStart(start: Int, text: String): Int {
var i = 0
var newStart = start
while (i < newStart) {
if (text[i] == SEPARATED_CHAR) {
newStart++
}
i++
}
return newStart
}
}
companion object {
const val LOCALE_LANGUAGE_FA = "fa"
const val SEPARATED_CHAR = '٬'
}
}
@behdad222
Copy link
Author

input.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment