I want to insert a space every 2 digits when inputting a string in edittext
For example, if you have the string 'abcdefghijk'
I want the edittext to come out like this 'ab cd ef gh ij k'
What should I do? thank you
The best way of doing this is to do something similar that this answer is doing.
For your code it would mean something like this:
class SpacedTextWatcher(private val editText: EditText) : TextWatcher {
override fun afterTextChanged(s: Editable?) {
editText.removeTextChangedListener(this)
val initialLength = editText.text.length
val v = s.toString().chunked(2).joinToString(" ")
val cp = editText.selectionStart
editText.setText(v)
val endLength = editText.length()
val selection = (cp + (endLength - initialLength))
if (selection > 0 && selection <= editText.text.length) {
editText.setSelection(selection)
} else {
editText.setSelection(editText.text.length - 1)
}
editText.addTextChangedListener(this)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
}
This answer is not handling special use-cases where there is a space in the string or anything else, but I think you can figure out the rest yourself. Just make sure you register this textWatcher on the editText you want to format.
Here is the exact solution what you want:
fun editTextSetContentMemorizeSelection(editText: EditText, charSequence: CharSequence) {
var selectionStart = editText.selectionStart
var selectionEnd = editText.selectionEnd
editText.setText(charSequence.toString())
if (selectionStart > charSequence.toString().length) {
selectionStart = charSequence.toString().length
}
if (selectionStart < 0) {
selectionStart = 0
}
if (selectionEnd > charSequence.toString().length) {
selectionEnd = charSequence.toString().length
}
if (selectionEnd < 0) {
selectionEnd = 0
}
editText.setSelection(selectionStart, selectionEnd)
}
fun formatStrWithSpaces(can: CharSequence): String? {
val sb = StringBuffer()
for (i in 0 until can.length) {
if (i != 0 && i % 2 == 0) {
sb.append(' ')
}
sb.append(can[i])
}
return sb.toString()
}
In your onCreate or where ever you want to use textwatcher:
editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
val origin = s.toString().replace(" ".toRegex(), "")
val formatStr = formatStrWithSpaces(origin)
if (s.toString() != formatStr) {
editTextSetContentMemorizeSelection( editText, formatStr!!)
if (before == 0 && count == 1 && formatStr!![ editText.getSelectionEnd() - 1] == ' ') {
editText.setSelection(editText.getSelectionEnd() + 1)
}
}
}
override fun afterTextChanged(s: Editable) {}
})
Here is the Source of this answer. You can also check.
i am trying to get a phone number from editText in my project. the problem i'm having;
1 -> the first digit should not be 0.
2 -> unfortunately but I can't delete spaces..
The number format I want to get; (555) 555 55 55
The stage my code has come to;
class PhoneNumberMask(val editText: EditText) : TextWatcher {
var phoneNumber: String = ""
var isRunning: Boolean = false
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable) {
if (isRunning || s.length == 15) {
return
}
isRunning = true
phoneNumber = when (s.length) {
0 -> "test"
1 -> "($s"
4 -> "$s) "
9 -> "$s "
12 -> "$s "
else -> s.toString()
}
editText.setText(phoneNumber).also { editText.setSelection(phoneNumber.length) }
isRunning = false
}
}
I think your strategy is going to have troubles when the user moves the cursor to the middle of the text field, backspaces, or tries to paste text. What I would try is filtering to only digits each time it's changed and insert parentheses/spaces for the whole string of digits every time. Then put the cursor back where they had it. You can figure out where the cursor should be more easily from onTextChanged than from afterTextChanged.
Something like this:
class PhoneNumberMask(val editText: EditText) : TextWatcher {
private var isRunning: Boolean = false
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (isRunning) {
return
}
var cursorPosition = start + count
val digits = s.filter(Char::isDigit)
.dropWhile { it == '0' }
.take(10)
cursorPosition -= s.take(cursorPosition).run {
count { !it.isDigit() } + filter(Char::digit).takeWhile { it == '0' }.count()
}
val output = StringBuilder(digits)
fun punctuate(position: Int, punctuation: String) {
if (digits.length > position) {
output.insert(position, punctuation)
if (cursorPosition > position) {
cursorPosition += punctuation.length
}
}
}
punctuate(8, " ")
punctuate(6, " ")
punctuate(3, ") ")
punctuate(0, "(")
isRunning = true
editText.setText(output)
editText.setSelection(cursorPosition.coerceAtMost(output.length))
isRunning = false
}
override fun afterTextChanged(s: Editable) {
}
}
I'm making a fractions calculator and have finally settled on the design and how things work and have written the code. However, it almost never gives out the correct answer. I don't know why this is happening?
It uses a Fraction class I made which contains only 3 variables: numerator, denominator and whole.
The process involves the following:
Taking input from user and converting to a Fraction form
Calculating
Simplifying the result
and finally displaying the result.
The code:
package com.example.fraccalcinkotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.Toast
import androidx.core.text.isDigitsOnly
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editText_number1_num.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number1_den.requestFocus()
editText_number1_num.setText(removeLastChar(editText_number1_num.text.toString()))
}
}
})
editText_number1_den.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
if (s.endsWith(" ") || s.endsWith("/")){
editText_number1_den.setText(removeLastChar(editText_number1_den.text.toString()))
}
}
})
editText_number1_whole.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number1_num.requestFocus()
editText_number1_whole.setText(removeLastChar(editText_number1_whole.text.toString()))
}
}
})
editText_number2_num.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number2_den.requestFocus()
editText_number2_num.setText(removeLastChar(editText_number2_num.text.toString()))
}
}
})
editText_number2_den.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ") || s.endsWith("/")){
editText_number2_den.setText(removeLastChar(editText_number2_den.text.toString()))
}
}
})
editText_number2_whole.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.endsWith(" ")){
editText_number2_num.requestFocus()
editText_number2_whole.setText(removeLastChar(editText_number2_whole.text.toString()))
}
}
})
button_add.setOnClickListener {
var sum:Fraction = AddNumbers()
var num: Int = sum.numerator
var den: Int = sum.denominator
var whole: Int = sum.wholeNo
var result_string: String = "$whole $num/$den"
textView_result.text = result_string
}
}
fun AddNumbers(): Fraction {
//convert String to Fraction for calculations
val num1: Int = editText_number1_num.text.toString().toInt()
val den1: Int = editText_number1_den.text.toString().toInt()
val whole1: Int = editText_number1_whole.text.toString().toInt()
val fraction1: List<Int> = listOf(num1, den1, whole1)
val num2: Int = editText_number2_num.text.toString().toInt()
val den2: Int = editText_number2_den.text.toString().toInt()
val whole2: Int = editText_number2_whole.text.toString().toInt()
val fraction2: List<Int> = listOf(num2, den2, whole2)
var frac_1: Fraction = fractionalize(fraction1)
var frac_2: Fraction = fractionalize(fraction2)
//extracting numerators and denominators from List<Int>
var numerator1 = frac_1.numerator
var denominator1 = frac_1.denominator
var numerator2 = frac_2.numerator
var denominator2 = frac_2.denominator
//Getting common denominator and numerators for adding
var commonDenominator: Int = 1
if (denominator1 != denominator2){
commonDenominator = getLCM(denominator1, denominator2)
numerator1 *= commonDenominator/denominator1
numerator2 *= commonDenominator/denominator2
} else if (denominator1 == denominator2){
commonDenominator = denominator1
}
//adding....
var sumOfNumerators: Int = numerator1 + numerator2
var result: Fraction = Fraction(sumOfNumerators, commonDenominator,0)
//simplifying....
result = simplify(result)
return frac_1
}
fun fractionalize(fraction: List<Int>): Fraction{
//Finds type of number (input) and converts to List<Int> with [numerator, denominator, whole number]
var num = fraction[0]
var den = fraction[1]
var whole = fraction[2]
if (whole != 0){
num += whole * den
whole = 0
}
val result: Fraction = Fraction(num, den, whole)
return result
}
fun simplify(fraction:Fraction):Fraction{
var numerator: Int = fraction.numerator
var denominator: Int = fraction.denominator
var whole_number: Int = fraction.wholeNo
if (getHCF(numerator, denominator) != 1){
numerator /= getHCF(numerator, denominator)
denominator /= getHCF(numerator, denominator)
}
if (numerator> denominator){
numerator %= denominator
whole_number = numerator / denominator
}
if (numerator == denominator){
whole_number += 1
numerator = 0
denominator = 1
}
var result: Fraction = Fraction(numerator, denominator, whole_number)
return result
}
fun getHCF(num1: Int, num2: Int):Int{
var dividend = Math.max(num1, num2)
var divisor = Math.min(num1, num2)
var remainder: Int = 1
//calculating HCF using repeated division method
do {
remainder = dividend % divisor
dividend = divisor
divisor = remainder
} while (remainder > 0)
return dividend
}
fun getLCM(num1: Int, num2: Int): Int{
//num1 * num2 = HCF * LCM
//so, LCM = (num1 * num2) / HCF
var result: Int = (num1 * num2)/getHCF(num1, num2)
return result
}
fun removeLastChar(string: String): String{
var result: String = string
if (!string.isEmpty()){
result = string.substring(0, string.length - 1);
}
return result
}
}
Please suggest me things I can do to find out which part is causing the problem.
I think I see the error here in simplify:
if (numerator> denominator){
numerator %= denominator
whole_number = numerator / denominator
}
You have already changed the numerator variable to be the remainder, and then you use it to find the whole number. You need to swap the order of these two lines in the if statement.
It would be better practice to have all these operations on Fraction be part of the Fraction class itself. This is called encapsulation and is fundamental to robust software design. Bugs will be rarer and easier to resolve. Something like this (I didn't test the math):
data class Fraction(val num: Int, val den: Int, val whole: Int) {
fun toPure(): Fraction {
return Fraction(num + whole * den, den, 0)
}
fun toSimplified(withWhole: Boolean): Fraction {
with(toPure()) {
val gcm = findGcf(num, den)
val simplified = Fraction(num / gcm, den / gcm, 0)
return if (!withWhole || num < den)
simplified
else
Fraction(simplified.num % simplified.den, simplified.den, simplified.num / simplified.den)
}
}
operator fun plus(other: Fraction): Fraction {
val newDen = den * other.den
val newNum = num * other.den + other.num * den
return Fraction(newNum, newDen, whole + other.whole).toSimplified(true)
}
private fun findGcf(num1: Int, num2: Int): Int {
var dividend = max(num1, num2)
var divisor = min(num1, num2)
var remainder: Int = 1
//calculating HCF using repeated division method
do {
remainder = dividend % divisor
dividend = divisor
divisor = remainder
} while (remainder > 0)
return dividend
}
}
To avoid repeating yourself, you can create a function for converting EditText content to a Fraction:
private fun getFraction(numET: EditText, denET: EditText, wholeET: EditText): Fraction? {
val num = numET.text.toIntOrNull() ?: return null
val den = denET.text.toIntOrNull() ?: return null
val whole = wholeT.text.toIntOrNull() ?: return null
return Fraction(num, den, whole)
}
Then your addNumbers function is a lot simpler:
fun addNumbers(): Fraction? {
val first = getFraction(editText_number1_num, editText_number1_den, editText_number1_whole) ?: return null
val second = getFraction(editText_number2_num, editText_number2_den, editText_number2_whole) ?: return null
return first + second
}
I made the return type nullable for the case of invalid input. Your click listener that uses addNumbers can show an error message instead of the Fraction if the input is invalid.
how to put # character before the word in edittext?
what I want : I wanna put # character before the every word written in edit text
every word starts with #
for example :
#hello #world #hi
Try this TextWatcher.
class SharpWordsTextWatcher: TextWatcher {
private var addSharp: Boolean = false
private var isEmpty = false
override fun afterTextChanged(s: Editable) {
if(addSharp) {
s.insert(s.length - 1, "#")
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
isEmpty = s.isEmpty() || s.last() == ' '
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
addSharp = isEmpty && s.last() != ' ' && count != 0
}
}
And add this to your EditText.
myEditText.addTextChangedListener(SharpWordsTextWatcher())
I am trying to figure out how to validate 6 EditText input fields and enable button
button_step_one_next_FSF.isEnabled = true
when everything fits my condition. I want to validate everything using this util class without creating TextWatcher object.
Here is my editText util class
inline fun EditText.onTextChange(crossinline f: (s: CharSequence?) -> Unit) {
val listener = object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
f(s)
}
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
}
addTextChangedListener(listener)
}
Here is short validation method example
private fun validateInput() {
edit_text_name.onTextChange { s ->
val name: String = s?.toString() ?: ""
if (!name.isNameNotValid()) {
text_input_name.isEndIconVisible = true
text_input_name.isErrorEnabled = false
} else {
text_input_name.error = getString(R.string.error_not_valid_name)
text_input_name.isEndIconVisible = false
}
}
edit_text_surname.onTextChange { s ->
val surname: String = s?.toString() ?: ""
if (!surname.isNameNotValid()) {
text_input_surname.isEndIconVisible = true
text_input_surname.isErrorEnabled = false
} else {
text_input_surname.error = getString(R.string.error_not_valid_surname)
text_input_surname.isEndIconVisible = false
}
}
I just added this method checkButtonEnableState() at the end of each validation in TextWatcher lambda expression and it solved my problem!
private fun checkButtonEnableState() {
button_step_one_next_FSF.isEnabled =
(!edit_text_name.text.toString().isNameNotValid()
&& !edit_text_surname.text.toString().isNameNotValid()
&& edit_text_password_FSF.text.toString().isValidPassword()
&& edit_text_password_confirm_FSF.text.toString().isValidPassword()) &&
(edit_text_password_confirm_FSF.text.toString() == edit_text_password_FSF.text.toString())
}