Check if EditText is empty while calculating numbers - android

I'm trying to build an age calculator application. When I didn't enter a number in one of the EditTexts and click on calculate Button, my app is crashed! "App has stopped" and click the photo
I tried to set yearbirth.isEmpty() || yearbirth.equals("") || yearbirth == null , but nothing worked.
CODE:-
class MainActivity : AppCompatActivity() {
var yearage:Int?= null
var monthage:Int?= null
var dayage:Int?= null
#SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
buGetAge.setOnClickListener({
val year= Calendar.getInstance().get(Calendar.YEAR)
val month= Calendar.getInstance().get(Calendar.MONTH)
val day= Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
var yearage = year - Integer.parseInt(yearbirth.text.toString()) -1
val monthage:Int?
val dayage:Int?
if (yearbirth== null) {
Toast.makeText(this, "You have to enter your year berth", Toast.LENGTH_LONG).show()
} else {
if (month < Integer.parseInt(monthbirth.text.toString())){
monthage=(Integer.parseInt(monthbirth.text.toString())- month- 12)* -1
}else if (month > Integer.parseInt(monthbirth.text.toString())) {
yearage=year- Integer.parseInt(yearbirth.text.toString())
monthage= (Integer.parseInt(monthbirth.text.toString())- month)* -1
}else {
yearage=year- Integer.parseInt(yearbirth.text.toString())
monthage=0
}
if (day < Integer.parseInt(daybirth.text.toString())){
dayage= (Integer.parseInt(daybirth.text.toString())- day- 30)* -1
}else if (day > Integer.parseInt(daybirth.text.toString())){
dayage= day- Integer.parseInt(daybirth.text.toString())
}else {
dayage=0
}
val a= (yearage* 12)+ monthage
val b= (a * 30)+ dayage
val ageinyears= "$yearage years"
val ageinmonths= "$a months"
val ageindays= "$b days"
txtshow.text = "Your age is $yearage years, $monthage months and $dayage days"
txtshow2.text = "\nYOUR AGE:\nin years:\n\nin months:\n\nin days:"
txtshow3.text = "\n\n$ageinyears \n\n$ageinmonths \n\n $ageindays "
this.yearage=yearage
this.monthage=monthage
this.dayage=dayage
}
})
}
}

Verify that yearbirth.text.toString() is not empty before attempting to parse it into an Integer (and also if it's a valid integer.)
To achieve so, put the line you showed in your code:
var yearage = year - Integer.parseInt(yearbirth.text.toString()) -1
Inside an if verifying that the input is valid, like so:
if(yearbirth.text.toString().matches(Regex("\\d+")))
var yearage = year - Integer.parseInt(yearbirth.text.toString()) -1

You can check for empty Input like this:
newString = Integer.toString(yearbirth).trim();
if (newString.matches("")) {
Toast.makeText(this, "EditText is empty", Toast.LENGTH_SHORT).show();
return;
}

Related

how to handle new line in word count?

i'm referring this answer https://stackoverflow.com/a/42802060/12428090
basically my goal is to handle word count of 100 words
the abve solution works fine for typed 100 words and even handles copy paste very well
but it doesnt handles the new line case
suppose in entered word or copy pasted word contains new line then the word count is returning incorrect
following is the code please help out
override fun onTextChanged(s: CharSequence, start: Int, before: Int,
count: Int) {
val wordsLength: Int = countWords(s.toString()) // words.length;
limitmoderator.text = "$wordsLength/$MAX_WORDS"
val yourText: String =
moderator_intro.getText().toString().replace(160.toChar().toString(), " ")
if (yourText.split("\\s+".toRegex()).size > MAX_WORDS) {
var space = 0
var length = 0
for (i in 0 until yourText.length) {
if (yourText[i] == ' ') {
space++
if (space >= MAX_WORDS) {
length = i
break
}
}
}
if (length > 1) {
moderator_intro.getText()
.delete(length, yourText.length) // deleting last exceeded words
setCharLimit(moderator_intro, length - 1) //limit edit text
}
} else {
removeFilter(moderator_intro)
}}
private fun countWords(s: String): Int {
val trim = s.trim()
return if (trim.isEmpty()) 0 else trim.split("\\s+".toRegex()).size
// separate string around spaces
}
private var filter: InputFilter? = null
private fun setCharLimit(et: EditText, max: Int) {
filter = LengthFilter(max)
et.filters = arrayOf<InputFilter>(filter as LengthFilter)
}
private fun removeFilter(et: EditText) {
if (filter != null) {
et.filters = arrayOfNulls(0)
filter = null
}
}
so i have tried rplacing the "\n" in the text but it doesnt seems to be handling the case properly
any help will be appreciated
thanks in advance
Here's a different strategy than the one from the question you linked. Notice I'm using afterTextChanged and not onTextChanged!
I'm manually counting words to get the character index of the first whitespace after the last allowable word. That way I don't have to trim and then use Regex, and then try to figure out the index offset of that Regex. Then instead of applying a temporary filter, I directly cut the end of the Editable off.
editText.setSelection is to keep the cursor from jumping to the beginning.
override fun afterTextChanged(s: Editable) {
var previousWasWhitespace = true
var i = 0
var wordCount = 0
for (c in s) {
val whitespace = c.isWhitespace()
if (whitespace && !previousWasWhitespace && ++wordCount == MAX_WORDS) {
break
}
previousWasWhitespace = whitespace
i++
}
if (i < s.length) {
s.delete(i, s.length)
editText.setSelection(i)
}
}
You could write a regular expression to match the text that you want to keep and remove the rest. In this case you want match (non-whitespace+)(whitespace*) maximum 100 times.
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
val input = moderator_intro.getText().toString()
val maxWords = 100
val whiteSpace = "\\p{javaWhitespace}\u00A0\u2007\u202F" //All white space characters
val keepRegex = Regex("[$whiteSpace]*([^$whiteSpace]+[$whiteSpace]*){1,${maxWords.toString()}}")
val allowedText = keepRegex.find(input)?.value ?: ""
val wordAmount = allowedText.split(Regex("[$whiteSpace]+")).filter { it.isNotBlank() }.size
val trailingWhiteSpace = Regex("[$whiteSpace]+$")
if(wordAmount == maxWords && allowedText.contains(trailingWhiteSpace)) {
val newText = allowedText.replace(trailingWhiteSpace, "")
moderator_intro.getText().delete(allowedText.length, input.length)
setCharLimit(moderator_intro, newText.length)
} else {
removeFilter(moderator_intro)
}
}

How to check if Number TextEdit is empty?

so I have this problem where I am trying to make Random Number Generator app on android. Basically you set the minimum and the maximum number and then it randomly picks numbers between min and max.
However my problem comes if the min or max TextEdit field is empty, the app crashes. I would like to display "X" on the screen. How to check if the field is empty or not?
I am using kotlin and here is sample of my code. I am begginer so please do not flame me if the code is wrong :)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton = findViewById<Button>(R.id.rollButton)
val resultsTextView = findViewById<TextView>(R.id.resultsTextView)
//val seekBar = findViewById<SeekBar>(R.id.seekBar)
val min = findViewById<TextView>(R.id.number_min)
val max = findViewById<TextView>(R.id.number_max)
rollButton.setOnClickListener {
if(min.text.toString().toInt()>= 0 && max.text.toString().toInt() <= 1000){
val rand = Random.nextInt(min.text.toString().toInt(),max.text.toString().toInt()+1)
resultsTextView.text = rand.toString()
}
else if(min.text.toString().isNullOrBlank()){
resultsTextView.text = "X"
}
else{
resultsTextView.text = "X"
}
}
}
}
To check if your EditText is empty use isNullOrEmpty(). This will check if your field is empty or is null, like the method name says. Here is an example:
val editTextString :String = editText.text.toString()
if(!editTextString.isNullOrEmpty()) //returns true if string is null or empty
There is another approach with TextUtils but since you are using Kotlin this approach is better.
EDIT:
You are doing this:
if(min.text.toString().toInt()>= 0 && max.text.toString().toInt() <= 1000){
val rand = Random.nextInt(min.text.toString().toInt(),max.text.toString().toInt()+1)
resultsTextView.text = rand.toString()
}
and here this line min.text.toString().toInt() is throwing you an exception. The reason for this is because currently min or max are empty String. So compailer can't format number from an String equals to "". You should do it like this:
if(!min.text.toString().isNullOrEmpty() && !max.text.toString().isNullOrEmpty() && min.text.toString().toInt()>= 0 && max.text.toString().toInt() <= 1000){
val rand = Random.nextInt(min.text.toString().toInt(),max.text.toString().toInt()+1)
resultsTextView.text = rand.toString()
}
I hope this works. If not, then take this into two IF statements like this:
if(min.text.toString().isNullOrEmpty() || max.text.toString().isNullOrEmpty() {
resultsTextView.text = "X"
} else if(min.text.toString().toInt() >= 0 && max.text.toString().toInt() <= 1000) {
val rand = Random.nextInt(min.text.toString().toInt(), max.text.toString.toInt()+1)
resultsTextView.text = rand.toString()
}
The second approach is maybe an even better and cleaner version since you don't have to check for anything else later.

Typechecking in Kotlin

I am a Kotlin newbie. I have a simple app in which I'm trying to sanitise all my input. Everything works in theory, but my app doesn't produce the required output.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val editTextWeight = findViewById<EditText>(R.id.weightEditText) as EditText
val editTextHeight = findViewById<EditText>(R.id.heightEditText) as EditText
val calculateButton = findViewById<Button>(R.id.calcButton)
calculateButton.isEnabled = false
val weight = editTextWeight.text.toString().toDoubleOrNull()
val height = editTextHeight.text.toString().toDoubleOrNull()
if (weight != null && height != null ) {
calculateButton.isEnabled = true
calculateButton?.setOnClickListener()
{
val bmi = weight / (height*height )
Toast.makeText(this#MainActivity,
"Your BMI is $bmi", Toast.LENGTH_LONG).show()
}
}
}
}
I have tried to add an else condition ie:
//calculateButton.isEnabled = false
if (weight != null && height != null ) {
//calculateButton.isEnabled = true
calculateButton?.setOnClickListener()
{
val bmi = weight / (height*height )
Toast.makeText(this#MainActivity,
"Your BMI is $bmi", Toast.LENGTH_LONG).show()
}
}else
Toast.makeText(this#MainActivity,
"Please enter both values correctly!", Toast.LENGTH_LONG).show()
The button is clickable, but I get no output from the app.
What am I doing wrong? Is there an app state like in Angular where I can seperate component initialisation from program logic?
You should check the text input when you perform click on your button, in perfect programm your code should look like this :
class MainActivity : AppCompatActivity() {
lateinit var calculateButton: Button
lateinit var editTextWeight: EditText
lateinit var editTextHeight: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
editTextWeight = findViewById(R.id.weightEditText)
editTextHeight = findViewById(R.id.heightEditText)
calculateButton = findViewById(R.id.calcButton)
calculateButton.setOnClickListener()
{
val weight: Double = editTextWeight.text.toString().toDoubleOrNull() ?: 0.0
val height: Double = editTextHeight.text.toString().toDoubleOrNull() ?: 0.0
val bmi = weight / (height * height)
if (bmi.isNaN())
Toast.makeText(this#MainActivity,
"Input error, please try again!", Toast.LENGTH_LONG).show()
else
Toast.makeText(this#MainActivity,
"Your BMI is $bmi", Toast.LENGTH_LONG).show()
}
}
}

How do I simplify all of these query parameter possible combinations in Kotlin? Builder Pattern?

I'm building an Android App with Kotlin that uses various NYTimes APIs to fetch different news data..
Here I have a search screen where I want the user to be able to enter a search query (i.e. "Japan") and check off any checkbox and they will even be able to add a begin or end date to refine their search :
Typing something in the search query and checking off at least one box are the only requirements, everything else will be mandatory. And once they hit "Search", it will pass the data they entered to a second activity which will use the data to put together an api call, make the api call, and populate a recyclerview , like so:
Now here is my issue...
I have the SEARCH button only sending data if the search query has been entered and if the travel checkbox has been checked, and as you can imagine there are a TON of combinations.. I don't know how I can pass over all of those combinations and make each API Call accordingly without having an extremely long If/Else Block that'll take forever...
Using Kotlin, there has to be a more efficient way right ?
Here is my Search Activity:
class SearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
search_query_edittext.getBackground().clearColorFilter();
actionBar?.setDisplayHomeAsUpEnabled(true)
Enter_Begin_Date.setPaintFlags(Enter_Begin_Date.getPaintFlags())
Enter_End_Date.setPaintFlags(Enter_End_Date.getPaintFlags())
// Calendar
val c = Calendar.getInstance()
val year = c.get(Calendar.YEAR)
val month = c.get(Calendar.MONTH)
val day = c.get(Calendar.DAY_OF_MONTH)
// TextView Clicked to show Date Picker Dialog
Enter_Begin_Date.setOnClickListener {
val dpd = DatePickerDialog(
this,
DatePickerDialog.OnDateSetListener { view, Year, Month, Day ->
// set to textView
if (Day < 10 && Month < 10) {
Enter_Begin_Date.text =
"0" + Day + "/0" + Month.toInt().plus(1) + "/" + Year
} else if (Day < 10 && Month >= 10) {
Enter_Begin_Date.text = "0" + Day + "/" + Month.toInt().plus(1) + "/" + Year
} else if (Day >= 10 && Month < 10) {
Enter_Begin_Date.text = "" + Day + "/0" + Month.toInt().plus(1) + "/" + Year
}
},
year,
month,
day
)
// show dialog
dpd.show()
}
Enter_End_Date.setOnClickListener {
val dpd = DatePickerDialog(
this,
DatePickerDialog.OnDateSetListener { view, Year, Month, Day ->
// set to textView
if (Day < 10 && Month < 10) {
Enter_End_Date.text = "0" + Day + "/0" + Month.toInt().plus(1) + "/" + Year
} else if (Day < 10 && Month >= 10) {
Enter_End_Date.text = "0" + Day + "/" + Month.toInt().plus(1) + "/" + Year
} else if (Day >= 10 && Month < 10) {
Enter_End_Date.text = "" + Day + "/0" + Month.toInt().plus(1) + "/" + Year
}
},
year,
month,
day
)
// show dialog
dpd.show()
}
searchButton.setOnClickListener {
if ((search_query_edittext.text.isNotEmpty()
&& Enter_Begin_Date.text.isEmpty()
&& Enter_End_Date.text.isEmpty()) && TravelTextBox.isChecked && !SportsTextBox.isChecked && !PoliticsTextBox.isChecked && !EntrepreneursTextBox.isChecked && !BusinessTextBox.isChecked && !ArtsCheckBox.isChecked
) {
val searchQuery: String = search_query_edittext.text.toString()
val query = searchQuery
val travelCategory: String = "Travel"
val intent = Intent(this#SearchActivity, ResultsActivity::class.java)
intent.putExtra("query", query)
intent.putExtra("travelCategory", travelCategory)
startActivity(intent)
}
}
}
}
My Result Activity:
class ResultsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_results)
val bundle: Bundle? = intent.extras
val myQuery = bundle!!.getString("query")
val myTravelCategory = bundle!!.getString("travelCategory")
if (bundle.containsKey("query") && bundle.containsKey("travelCategory")) {
lifecycleScope.launch(IO) {
val result = ApiClient.getClient.getSearchResultWithCheckbox(query = myQuery!!,
category = myTravelCategory!!,
api_key = (MYAPIKEY)
withContext(Main) {
SearchResultsRecyclerView.apply {
layoutManager = LinearLayoutManager(this#ResultsActivity)
adapter = SearchResultsAdapter(result.response.docs)
}
}
}
}
}
}
As you can see, I only have coded enough to cover for the user to enter in the search query , to check the travel checkbox and to hit enter, there are like 145 more combinations (search query + begin date + politics checkbox, search query + end date + sports checkbox, etc etc..)
How can I add in all of the other combinations without using an extremely long If/Else block?
Anything to push me in the right direction would be highly appreciated , thanks!
check for the condition you want, not the combinations that produce it.
Check if the search query is filled and that at least one checkbox is checked. Without knowing how the API works is hard to recommend a particular way to pass the data but taking advantage of nullable types and/or optional paramaters with something like this should work:
private fun getCheckedCategories() : List<String> = listOfNotNull(
"Travel".takeIf { TravelTextBox.isChecked },
...
)
private fun atLeastOneCheckBoxChecked() = getCheckedCategories().isNotEmpty()
With this helper functions you can build a listener similar to this:
searchButton.setOnClickListener {
if (search_query_edittext.text.isNotEmpty() && atLeastOneCheckBoxChecked()) {
val searchQuery: String = search_query_edittext.text.toString()
val checkedCategories = getCheckedCategories()
val beginDate : String? = TODO()
val endDate : String? = TODO()
val intent = Intent(this#SearchActivity, ResultsActivity::class.java)
intent.putExtra("query", searchQuery)
intent.putStringArrayList("checkedCategories", ArrayList(checkedCategories))
intent.putExtra("beginDate", beginDate)
intent.putExtra("endDate", endDate)
startActivity(intent)
}
}
In the other end the bundle?.getString("beginDate") will return null if not passed, and whatever value it has if passed. Or retrieve the list of passed checkboxes with bundle?getStringArrayList("checkedCategories").orEmpty()

Android Multi-row summation: Request for code shortening

I have a table with fifteen rows. Each row have three columns and a total column. I want to get the total per row, the grand total, and the overall average.
The user may not enter data for all rows, and the user may skip a row.
So the code checks if the user have entered data in one of three fields of each row.
If the row is blank, ignore it.
If some of the fields are filled-up, tell the user to fill up the rest of the row.
If all the fields in a row is filled up, sum all its fields and increment the divider.
I have only pasted the codes for Rows 1 & 2 for brevity, but it shows the gist of what I'm trying to achieve:
The code:
var a1 = 0
var a2 = 0
var total = 0
var divider = 0
// Row 1
if (b1p1.text.isNotEmpty() or b2p1.text.isNotEmpty() or b3p1.text.isNotEmpty()) {
var y = 0
listOf(b1p1, b2p1, b3p1).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p1, b2p1, b3p1).forEach {
a1 += it.text.toString().toInt()
}
total1.text = a1.toString()
total += a1
e2 = 1
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
// Row 2
if (b1p2.text.isNotEmpty() or b2p2.text.isNotEmpty() or b3p2.text.isNotEmpty()) {
var y = 0
listOf(b1p2, b2p2, b3p2).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p2, b2p2, b3p2).forEach {
a2 += it.text.toString().toInt()
}
total2.text = a2.toString()
total += a2
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
if (e2 == 1) {
grandTotalTextView.text = total.toString()
average = total.toDouble()/divider
val decimalFormatter = DecimalFormat("#,###.##")
averageTextView.text = decimalFormatter.format(average).toString()
cyeSingleton.anct3b = decimalFormatter.format(average).toString()
} else {
Toast.makeText(activity, "Error 2", Toast.LENGTH_SHORT).show()
}
The table:
This is the best I could come up with. Should there be no other suggestion, I will settle for this.
Thanks in advance!
**EDIT: Thanks to ** https://stackoverflow.com/users/3736955/jemshit-iskenderov
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
private fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>): TotalResult {
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews as List<EditText>, totalTextViews[index])
if(!rowResult.ignoreRow){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
Toast.makeText(
activity,
"$divider, $allTotal, $showError", Toast.LENGTH_SHORT)
.show()
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
private fun calculateRowResult(rowTextViews:List<EditText>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.text.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
fun main(){
val totalResult = calculateTotalResult(
allTextViews = listOf(
listOf(t11,t12,t13),
listOf(t21,t22,t23)
),
totalTextViews = listOf(totalView1, totalView2)
)
// single Toast error
if(totalResult.showError){
// showToast(error)
}
// use totalResult.divider, totalResult.allTotal
}
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>){
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews, totalTextViews[index])
if(!rowResult.ignore){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
fun calculateRowResult(rowTextViews:List<TextView>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
Extracted calculateTotalResult() and calculateRowResult() so multiple rows and columns do not need to repeat same code.
calculateRowResult() processes singlet row of TextViews. I had to iterate rowTextViews twice, one to calculate ignore, the other to show error on TextView if not ignore. We don't show Toast Error here yet.
calculateTotalResult() iterates through all rows and gets total result. We show only one Toast Error (if required) after this step.
Code is pseudo-code, not tested.

Categories

Resources