I have a numberDecimal EditText (codeInput) in my app, and I want the output to be multiplied by a double (tipPercent) as it is being typed, so I can display it to a textView (totalText). I tried this code:
costInput.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) {
totalText.setText(s * tipPercent)
}
})
when I use this, however, I get this error:
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public inline operator fun BigDecimal.plus(other: BigDecimal): BigDecimal defined in kotlin
public inline operator fun BigInteger.plus(other: BigInteger): BigInteger defined in kotlin
what can I do to fix this? Thanks.
override fun onTextChanged(s: CharSequence, start: Int,
before: Int, count: Int) {
val input = Integer.parseInt(s.toString())
val res = input * tipPercent
totalText.setText("$res")
}
Try this:
costInput.doAfterTextChanged { text ->
val cost = text.toString().toDoubleOrNull()
totalText.text = if(cost == null) "" else totalText.text = "${cost * tipPercent}"
}
This will take care of invalid input in costInput.
Related
I have an extension function in kotlin to check is it a valid string or not as stated below.
fun EditText.onAfterTextChanged(listener: (String) -> Unit) {
addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
val input = editable?.toString()
val allowedChars = context.getString(R.string.supported_digits)
val newValue = replaceInvalidCharacters(input, allowedChars)
if (newValue != input) {
setText(newValue)
setSelection(text.length)
}
listener(newValue)
}
override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
}
private fun replaceInvalidCharacters(value: String?, allowedChars: String): String {
var finalValue = value ?: ""
if (finalValue.isNotEmpty()) {
val lastChar = finalValue.last()
if (!allowedChars.contains(lastChar, false)) {
finalValue = finalValue.dropLast(1)
}
}
return finalValue
}
I am using it like:
editText.onAfterTextChanged {
val length = it.length
if (length >= 250) {
activity?.toast(getString(R.string.max_limit_reached))
return#onAfterTextChanged
}
}
Here I want to pass allowedChars as a parameter to this extension as there are different strings are there for different EditText's in the application. Like 1 EditText may allow only number's but not +,- and some edit text may allow only alphanumeric, etc. Is there any way to pass a parameter to the extension?
What you can do is update the extension function signature by adding a parameter before the callback function. So, it'll look something like this
fun EditText.onAfterTextChanged(allowedChars: String, listener: (String) -> Unit) {
addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
val input = editable?.toString()
val allowedChars = context.getString(R.string.supported_digits)
val newValue = replaceInvalidCharacters(input, allowedChars)
if (newValue != input) {
setText(newValue)
setSelection(text.length)
}
listener(newValue)
}
override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
}
And you can call it like so:
editText.onAfterTextChanged("123abc") {
val length = it.length
if (length >= 250) {
activity?.toast(getString(R.string.max_limit_reached))
return#onAfterTextChanged
}
}
I'm trying to hide error on a text input layout after the user solves the error but I am not sure how to do that, given that the function afterTextChanged doesn't seem to work with it. How can I do this?
This is the part I'm talking about:
loginViewModel.loginFormState.observe(this, Observer {
val loginState = it ?: return#Observer
// disable login button unless fields are valid
binding.btnLogin.isEnabled = loginState.isDataValid
if (loginState.emailError != null) {
binding.etEmail.error = getString(loginState.emailError)
}
if (loginState.passwordError != null) {
binding.tfPassword.error = getString(loginState.passwordError)
}
})
binding.etEmail.afterTextChanged {
loginViewModel.loginDataChanged(
binding.etEmail.text.toString(),
binding.etPassword.text.toString()
)
}
binding.etPassword.afterTextChanged {
loginViewModel.loginDataChanged(
binding.etEmail.text.toString(),
binding.etPassword.text.toString()
)
}
private fun TextInputEditText.afterTextChanged(afterTextChanged: (String) -> Unit) {
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
afterTextChanged.invoke(editable.toString())
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
})
Even if the user writes the password correctly and they can click on a button I have, the error is still there, how can I make it go away?
I guess your error lies in those lines:
if (loginState.emailError != null) {
binding.etEmail.error = getString(loginState.emailError)
}
Now imagine emailError was "some error" once and now is null. The bindings' text will not be set again, so it's still "some error". To fix this mistake replace it with:
binding.etEmail.error = if(loginState.emailError != null){
getString(loginState.emailError)
} else {
null
}
Do the same for your passwordError
you can use setError method and pass null as argument. from documentation
Sets an error message that will be displayed below our EditText. If
the error is null, the error message will be cleared.
you can call this method in afterTextChanged as
textInputEditText.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) { }
override fun afterTextChanged(s: Editable?) {
textInputLayout.error = null
}
})
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 want to create mask for credit card mask for edittext in format like [0000] [0000] [0000] [0000], but not user should delete whitespaces manually
For example:
"4444_4444_4"
"444_4444_"
How to implement deleting of whitespace " " automatically?
https://github.com/RedMadRobot/input-mask-android
Try this. Hope it will help. (Kotlin example)
class CreditCardFormattingTextWatcher : TextWatcher {
private var etCard: EditText? = null
private var isDelete: Boolean = false
constructor(etcard: EditText) {
this.etCard = etcard
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
isDelete = before != 0
}
override fun afterTextChanged(s: Editable) {
val source = s.toString()
val length = source.length
val stringBuilder = StringBuilder()
stringBuilder.append(source)
if (length > 0 && length % 5 == 0) {
if (isDelete)
stringBuilder.deleteCharAt(length - 1)
else
stringBuilder.insert(length - 1, " ")
etCard?.setText(stringBuilder)
etCard?.setSelection(etCard?.text?.length ?: 0)
}
}
}