kotlin - Problem with my if statement and operator - android

This is my goal : user click on minus button the amount decrease by one and there is a if statement to not allow the amount go lower than 0 .
This is my Code :
var number = 0
view.text_amount.visibility = View.GONE
view.plus_btn.setOnClickListener {
if (number == 5) {
Toast.makeText(
requireContext(),
"Limit in order",
Toast.LENGTH_SHORT
).show()
} else {
view.text_amount.visibility = View.VISIBLE
number++
view.text_amount.text = number.toString()
}
}
view.minus_btn.setOnClickListener {
if (number <= 0) {
view.text_amount.visibility = View.GONE
} else {
number--
view.text_amount.text = number.toString()
}
}
there is problem with code : I don't want the amount be visible after getting to 0 . it's better experience when the amount is equal to 0 not be visible .
I think it has a simple solution but I can't see it .
do you have any idea ?

Your code works fine! If you want to make it simpler, there's a bit of repeated logic you could condense into one function that handles the situations:
fun adjustValue(amount: Int) {
val adjusted = number + amount
if (adjusted > 5) // show Toast
number = adjusted.coerceIn(0, 5)
view.text_amount.text = number.toString()
view.text_amount.visibility = if (number == 0) View.GONE else View.VISIBLE
}
view.minus_btn.setOnClickListener { adjustValue(-1) }
view.plus_btn.setOnClickListener { adjustValue(1) }
basically the idea is you work out the new value (I'm using a temporary variable so we never set number to an invalid value) and show whatever warnings you need to. Then the coerceIn line makes sure we lock it within the valid range of allowed values.
You could do if/else checks and only set the new value if it's a valid one, but sometimes it's simpler and shorter to just set things and then worry about the edge cases, so this is just an example of that!
Same thing for the TextView bit - it's easier to just set the value whatever it is, and then set whether it should be displayed or not. You could use if/else branches to look at the value and decide whether to set the text or not... but why make your life hard? We know the value's in the valid 0 to 5 range, we can hide it if it's 0 and show it otherwise... easy!
You could make the function take a Boolean instead, like plus: Boolean and then go val adjusted = number + if (plus) 1 else -1, but making it an Int means you could easily add a +10 button or whatever without adding any more code or any more validation logic, and it's not any more complicated to pass in -1 instead of false (arguably it's clearer!)
that's probably more than you were asking for but hopefully it's useful. If nothing else, the "just set the text and the visibility every time" approach is good and neat

Decrease the value of text_amount only if it contains a value greater than 0 and after that check again its value and if it is 0 then hide it:
view.minus_btn.setOnClickListener {
if (number > 0) {
number--
view.text_amount.text = number.toString()
if (number == 0) view.text_amount.visibility = View.GONE
}
}

Related

Kotlin EditText Hexeditor in overwrite mode that skips spaces being buggy

Sorry for the big amount of code guys. Im at a loss lol. I needed a way in an EditText to overwrite chars, but skip the two spaces when the curser comes to them. So that spaces will be "Permanent" in a sense. This is for a basic hexadecimal editor style text box. While this somewhat does work, an when it does its slick. But it seems to have some flaw I am missing, Sometimes when you re typing it will start inserting characters, specifically when the curser is in between two chars ex this is before : "01 02 0|3 04 05" if you type 5 it should replace the 3, then skip over the spaces and end up at the next 0 But it ends up either one of two things, either inserting "01 02 05|3 04 05" or overwriting the 3, and removing one of the two space while jumping "01 02 05 |04 05". lastly it used to sometimes replace a space when the curser was next to a pace but didn't jump over the two spaces, I believe I have worked this out but I've been working on the other problems so I may have been blinded a bit and not noticed it. I'm guessing my text watcher is not being invoked by either formating var not returning to false, or some other thing I've overlooked. Because once curser is moved IE you touch somewhere else in the text, it begins to work briefly till it ends up inserting in between digits again. Anyone see anything I may have missed?
Here is the code so far:
class CustomEditText : AppCompatEditText {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
private var isFormatting = false
private var mStart = -1 // Start position of the Text being modified
private var mEnd = -1 // End position of the Text being modified
private val watcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
mStart = -1
// Keep track of the start and end position of the text change
if (before == 0) {
// The start and end variables have not been set yet, and there are no characters being deleted.
// Set the start position to the current cursor position.
mStart = start + count
// Set the end position to either the end of the string or the current cursor position + count.
mEnd = min(mStart + count, s!!.length)
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// No action needed before text change
}
override fun afterTextChanged(s: Editable?) {
// Avoid infinite recursion if the TextWatcher is modifying the text
if (!isFormatting) {
if (mStart >= 0) {
// Set `isFormatting` to true to indicate that we're currently formatting the text.
isFormatting = true
// Check if the current position is a digit
if (Character.isDigit(s!![mStart - 1])) {
// Check if the next position is a space or the end of the string
if (mStart < s.length && s[mStart] == ' ' || mStart == s.length) {
// If the next position is a space or the end of the string, insert the digit at the next position
s.insert(mStart, s[mStart - 1].toString())
mStart++
} else {
// Overwrite the text at the current position
s.replace(mStart, mEnd, "")
}
} else if (s[mStart - 1] == ' ') {
// Check if the next position is a digit
if (mStart + 1 < s.length && Character.isDigit(s[mStart + 1])) {
// Jump over the spaces and overwrite the first character in the next set
mStart = s.indexOf(" ", mStart) + 2
s.replace(mStart, mStart + 1, "")
} else {
// Overwrite the text at the current position
s.replace(mStart, mEnd, "")
}
} else {
// Overwrite the text at the current position
s.replace(mStart, mEnd, "")
}
isFormatting = false
}
}
}
}
init {
// Initiate and add the text change listener "watcher"
addTextChangedListener(watcher)
}
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
// Get the current text in the EditText
val text = text
if (text != null) {
val len = text.length
// If the selection start and end are equal, meaning the cursor is at a certain position
if (selStart == selEnd) {
// Check if the cursor is positioned at a space between two hexadecimal digits
// And if the character after the space is also a space
if (selStart != 0 && selStart < len && text[selStart - 1] == ' ' && text[selStart + 1] == ' ') {
// Move the cursor one position to the right to position it at the start of the next hexadecimal digit
setSelection(selStart + 1)
return
}
// Check if the cursor is positioned at a space and the character after the space is not a space
if (selStart < len && text[selStart] == ' ' && (selStart + 1 >= len || text[selStart + 1] != ' ')) {
// Move the cursor one position to the right to position it at the start of the next hexadecimal digit
setSelection(selStart + 1)
}
}
}
// Call the superclass implementation of onSelectionChanged
super.onSelectionChanged(selStart, selEnd)
}
}
I've also toyed with using drawables for the spaces, I even thought that maybe if I make a custom drawable similar to a tictactoe board if you will, and have the digits in between the drawable to achieve the same result. I know either way I still have to handle backspaces and the arrow key movement in the even the user is using a keyboard, but that's a 3 day debug session for another time. If anyone has any ideas or see anything I missed that would be awesome, Or if you think this approach is not the best. I tired many different ways to approach this but this got the closest result to working. I do feel as though a drawable may be much more resource intensive than a text watcher, albeit this would be as well with larger files, but that can all be solved down the road. This is allot, and I don't expect a magical fix. But more eyes on a project might be able to spot what I've missed, thank you for your time =)
EDIT-----------
So it seems this is a buggy way to go about this type of use case, and very unreliable. Ive started working on instead overriding the onDraw method in EditText to simply draw the text in the positions. Hoping this isn't too resource intensive as I haven't ran the code on hardware yet to see but at any rate I'm assuming it will stay in place when edited and be pretty resource efficient as compared to other methods I've tried(Some even an S22 ultra had a hard time with). Then it's simply implementing overwrite mode. Which i have already done. Hopefully this pans out. If anyone has a better idea or if the above code can be made more reliable I would still love to see it! For now my efforts are going towards onDraw.

Create list/array of Textviews to change .text via index

So I was hoping to make a list/array of textviews so that I can iterate a loop and set the .text value of the TextViews as I go. Otherwise I would have to set the values in the code statically which would be a whole lot messier and potentially not even feasible for my needs.
So in the code below the idea would be to iterate the loop and when the correct value is confirmed that [index] would then set the corresponding
var refillToken : Double = (0).toDouble()
var tweetStored : BooleanArray = BooleanArray(20)
var tweetActive : BooleanArray = BooleanArray(20)
var userID: MutableList<String> = mutableListOf("")
var textViewToken = 0
while (refillToken > 0) {
var token: Int = 0
while (token < (tweetStored.size)) {
if (tweetStored[token] == true) {
tweetActive[token] = true
textView[textViewToken].text = userID[token]
textViewToken++
refillToken--
token++
if (refillToken < 0) {
break
}
}
}
}
}
I know my loop is probably messy by sane people standards but it makes sense to me and (hopefully) isn't the issue at play. Have found a few articles or ideas searching for the past two hours but they're either 10 years old (and I think deprecated), for java or don't work for whatever reason.
You need to get a value and then add it to the textview and change this value after every action on the page.
Use variable assignment for this task

Starts comparing from the first number, not the whole number in kotlin

Hello I need to compare 2 numbers and I used >, => but it doesn't compare whole number, it looks for the leftest(left) number and compare
for example the number is 92,236 and i want to compare it with 100,000, it says 92236 is bigger than 100,000 and it is because of the first number which is 9 and the first number of second number that is 1 so it says 100,000 is not bigger than 9236
here what I had done
class IncreaseMoneyFragment : Fragment() {
var decide = ""
val increaseEditText = mIncreaseMoneyBinding.increaseEdt.text.toString() (get value of edit text)
val currentPayment = it.payment (get loanPayment from database)
if (increaseEditText > currentPayment) {
Toast.makeText(activity, "more", Toast.LENGTH_SHORT).show()
val more = "بیشتر"
decide = more
} else {
Toast.makeText(activity, "less", Toast.LENGTH_SHORT).show()
val less = "کمتر"
decide = less
}
builder.setTitle(" مبلغ مورد نظر از مبلغ قسط وام $decide است. ادامه میدهید؟")
THANKS FOR HELPING ME :)
You are most likely comparing strings (text) and not numbers here. That's why it's using the alphabetical order instead of the integer order:
println("92236" > "100000") // true
println(92236 > 100000) // false
You probably want to convert your strings into integers instead:
if (increaseEditText.toInt() > currentPayment.toInt()) {
// ...
}
Note that toInt will crash if the strings are not actual numbers (for instance empty).
You can use toIntOrNull if you want more safety. It returns null if the string is not a number, so you can simply check for null and deal with this problem separately before comparing.

how to write logic of draw in Tac tac toe, kotlin

I am making tic tac toe in android studio on kotlin, I am new at this, I complete everything but i want to make that if none of combinations matchs then write on screen that it's draw but i cant write some logic or i missed something:
this is draw function code:
private fun draw() {
if (!firstPLayer.contains(1) && !firstPLayer.contains(2) && !firstPLayer.contains(3)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(2) && !secondPLayer.contains(3)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(4) && !firstPLayer.contains(5) && !firstPLayer.contains(6)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(4) && !secondPLayer.contains(5) && !secondPLayer.contains(6)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(7) && !firstPLayer.contains(8) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(7) && !secondPLayer.contains(8) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(1) && !firstPLayer.contains(4) && !firstPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(4) && !secondPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(2) && !firstPLayer.contains(5) && !firstPLayer.contains(8)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(2) && !secondPLayer.contains(5) && !secondPLayer.contains(8)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(3) && !firstPLayer.contains(6) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(3) && !secondPLayer.contains(6) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(1) && !firstPLayer.contains(5) && !firstPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(1) && !secondPLayer.contains(5) && !secondPLayer.contains(9)) {
resultView.text = "Draw!"
}
if (!firstPLayer.contains(3) && !firstPLayer.contains(5) && !firstPLayer.contains(7)) {
resultView.text = "Draw!"
}
if (!secondPLayer.contains(3) && !secondPLayer.contains(5) && !secondPLayer.contains(7)) {
resultView.text = "Draw!"
}
}
so, I am saying that if there isn't this combinations of number in lists, write "draw" in resultView
but it shows draw on first click on any button.
why?
The following assumes that playerOne and playerTwo are List<Int>s containing which of the nine squares from 1-9 the players have placed marks in.
Any time you see that you're copy-pasting code more than once (like resultView.text = "Draw!" and all the similar-looking if statements), you should rethink the design to simplify it.
I would create a collection of all possible win conditions:
private val winConditions = listOf(
listOf(1, 2, 3),
listOf(4, 5, 6),
listOf(7, 8, 9),
listOf(1, 4, 7),
listOf(2, 5, 8),
listOf(3, 6, 9),
listOf(1, 5, 9),
listOf(3, 5, 7),
)
Then each time a player makes a mark, you can check if either player won. If neither player won and nine moves have been made, the game is a draw.
// In a function called right after a player makes a move:
when {
winConditions.any { playerOne.containsAll(it) } -> showPlayerOneWins()
winConditions.any { playerTwo.containsAll(it) } -> showPlayerTwoWins()
playerOne.size + playerTwo.size == 9 -> showGameIsDraw()
else -> startNextPlayerTurn()
}
To get more fancy, you could have it only check the player who just made a move. That might make sense if it was a complicated algorithm to determine the winner, but Tic Tac Toe is trivial so I think it's just simpler to simply check them both so you can use the same code without extra parameters.
There are better ways to do what you're doing, like in Tenfour04's answer, but your basic approach is fine! Hard work, but it works. It's just the logic doesn't exactly fit what you're trying to do.
So, I'm assuming firstPlayer is a list (or some other collection) that holds the square numbers that player1 has marked with their X or O, and the same thing for secondPlayer. And I think what you're doing is checking all the winning combinations, and if nobody has any of those, the game is a draw. Is that right?
Your first problem is what you're running into - if you click a button, and draw() gets called, it tells you it's a draw. How come? Well, think about what you're doing - you're checking if anyone has a win condition, which they don't, because it's the start of the game. So the result is "draw!"
So really, you can't call draw() at any time, because the game has four possible states: player1 win, player2 win, draw (which are all finished states) and in progress, which basically means none of the end states have been reached yet. You're checking for the win states, but assuming that if they don't match, you must be in the draw state.
So, you'll either need to call draw() when you know one of those end states has been reached (which you can't really know without checking), or you'll need to explicitly check for a draw state along with the win state checks. (One thing you could think about, is whether all the squares have been filled.)
The other thing is that your logic doesn't really handle the win states properly. Imagine player 2 has the top line filled. The first if confirms player 1 doesn't have that line. They don't of course, so it prints "draw!" If you think about it, at most one of those win states can match, so either every other check is going to pass and set the result text to "draw!"
Typically the way you'd handle this kind of thing is to have a boolean flag set to a default, and set it when a check "trips the alarm" if you like. You only set it one way, so once it flips from the default to being set, another check won't flip it back
// assuming a draw until we know otherwise
var draw = true
// checking if player 1 has WON
if (firstPLayer.contains(1) && firstPLayer.contains(2) && firstPLayer.contains(3)) {
// when we know someone's won, we can say things like it's not a draw, maybe record who won
draw = false
}
...
// set the text either way, ensuring it always displays the correct thing
resultView.text = if (draw) "draw!" else ""
notice I swapped the if condition to check if player 1 has won, not if they've not won. This way we're looking for the specific condition where we can set the flag. It's easier to handle "was this condition was ever met" than "were any of these conditions never met".
It depends on the person, but sometimes it's easier to work these things out on paper - write down the questions you need to ask, to check if someone's won, someone's lost, is it a draw or is the game still going. Have a think about it!

List items iteration speed too slow when having another loop inside

I have two list, one with all possible devices and another with just few devices. I need pass final list with this condition:
if full list == one of the items in smaller list, make this item "active" too true, else leave it false.
I have no problem when working with full list >500 devices and small list >50, but when I have for example 2000 devices everything start to be too slow (on Google Pixel 2XL I need to wait about 6 seconds to job finish).
Question: how can I increase this loop speed?
What I have done so far:
devicesList.forEach { device ->
device.selected = false
items.forEach { it ->
if(it.id == device.id){
device.selected = true
}
}
But this is too slow for larger data
You can speed it up a bit by not using forEach, which uses an interator and instead use a for loop. You can also break once you locate your id, assuming they are unique
for (i in 0 until devicesList.size) {
val device = devicesList[i]
for (j in 0 until items.size) {
val item = items[j]
if (item.id == device.id) {
device.selected = true
break
}
}
}
Assuming your ids are unique, you could also make a duplicate of the items list and drop those that have been located, so each loop is shorter, like this
val copy = items.toMutableList()
for (i in 0 until devicesList.size) {
val device = devicesList[i]
for (j in 0 until copy.size) {
val item = copy[j]
if (item.id == device.id) {
device.selected = true
copy.remove(item)
break
}
}
}
You could also consider creating a map where the key is your id so you do not have to loop and instead you retrieve the item by id directly. You have to weight the cost of creating the map in the first place.
val map = items.associateBy { it.id }
for (i in 0 until devicesList.size) {
val device = devicesList[i]
device.selected = map[device.id] != null
}
Besides this, you should also move your logic to a background thread and wait for it to complete.
If all you need is make selected = true if a device's id exists in the list items, you can get all the ids of items like this:
val ids = items.map { it.id }
and then loop through devices:
devicesList.forEach { it.selected = it.id in ids }

Categories

Resources