rate stars in rating bar | kotlin - kotlin

i'm new in kotlin and i need help!!
i have list of 5 stars on the line(like rating bar) and want, for example when you clicked 3d star previous two change color. how can i write it with loop pragmaticaly? with if/else?
i hope you help me! thanks!!
task:
// Input - get elements or number or other data
// list of stars (items and index of items)
// rating number (x/5) (number of clicked star)
//
// Loops - FOR - iterate over elements
// Conditions - IF/ELSE - what to do with element
// Action:
// loop over stars (star & indexOfStar)
// set color: (yellow for start behind chosen one including chosen one (rating number))
have this code:
val star1: ImageView = binding.star1
binding.star1.setOnClickListener {
Log.d("Star 1","Rate 1")
star1.setImageDrawable(ContextCompat.getDrawable(star1.context, com.example.godelreviews.R.drawable.star2))}
val star2: ImageView = binding.star2
binding.star2.setOnClickListener {
Log.d("Star 2","Rate 2")
star2.setImageDrawable(ContextCompat.getDrawable(star1.context, com.example.godelreviews.R.drawable.star2))}
val star3: ImageView = binding.star3
binding.star3.setOnClickListener {
Log.d("Star 3","Rate 3")
star3.setImageDrawable(ContextCompat.getDrawable(star1.context, com.example.godelreviews.R.drawable.star2))}
val star4: ImageView = binding.star4
binding.star4.setOnClickListener {
Log.d("Star 4","Rate 4")
star4.setImageDrawable(ContextCompat.getDrawable(star1.context, com.example.godelreviews.R.drawable.star2))}
val star5: ImageView = binding.star5
binding.star5.setOnClickListener {
Log.d("Star 5","Rate 5")
star5.setImageDrawable(ContextCompat.getDrawable(star1.context, com.example.godelreviews.R.drawable.star2))}
return binding.root
}
private fun onRatingChanged(rating: Int){
Log.d("AddPage", "onRatingChanged: $rating/5")
}
private fun getStarRating(): Int{
TODO("Implement")
}
}
now it looks like this:
but i want mark others stars like this:

Since this looks like homework I'm not going to tell you how to do it, but the instructions you posted are really helpful!
Action:
loop over stars (star & indexOfStar)
set color: (yellow for start behind chosen one including chosen one (rating number))
It's saying you need to loop over all the stars. If the current star is the chosen one, or it's before the chosen one, you need to set it to yellow. Otherwise you need to set it to grey or whatever.
You need to do this whenever the currently selected star changes - so it makes sense to put this loop in a function you can call whenever that happens. That way, all your click listeners have to do is set the current star rating, and call the function that updates the display.
You already have a function declared that looks a lot like this:
private fun onRatingChanged(rating: Int){
...
}
It's called onRatingChanged and takes a rating value. How about you make that store the rating (if necessary), and then loop over your star ImageViews, updating each to display the appropriate star? Then your click listeners can just call that with the appropriate rating value.

Modify your code like this, It may work for you -
binding.star1.setOnClickListener {
Log.d("Star 2","Rate 2")
binding.star1.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))}
binding.star2.setOnClickListener {
Log.d("Star 2","Rate 2")
binding.star1.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star2.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))}
binding.star3.setOnClickListener {
Log.d("Star 3","Rate 3")
binding.star1.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star2.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star3.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))}
binding.star4.setOnClickListener {
Log.d("Star 4","Rate 4")
binding.star1.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star2.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star3.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star4.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))}
binding.star5.setOnClickListener {
Log.d("Star 5","Rate 5")
binding.star1.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star2.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star3.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star4.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))
binding.star5.setImageDrawable(ContextCompat.getDrawable(binding.star1.context, com.example.godelreviews.R.drawable.star2))}
return binding.root
}

Related

Kotlin how to disable a btn depending on condition

I have seen a code that do the following:
button.isEnabled = false
button.isClickable = false
I do not know if this was an older way of doing it. I am just not sure how it will be implemented into my code:
lateinit var dialog:AlertDialog
// Initialize an array of colors
var checked = 0
val items = arrayOf("CHECKED", "UNCHECKED")
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.dialogTitleDel)
builder.setSingleChoiceItems(items,-1) { _, which ->
checked = which + 1
}
builder.setPositiveButton("Yes"){dialogInterface, which ->
if( checked > 0){
modal.tvdone = 1
Log.e("Clicked", "Successful delivery")
notifyDataSetChanged()
}
// else{PositiveButton.setEnabled(false)}
}
dialog = builder.create()
dialog.setCancelable(false)
dialog.show()
What will be the correct way of disabling the positive button, until a condition is met?
It's not clear what you are trying to achieve
as per your commented line your are trying to disable the positive button on positive button click
Regardless, you need to get the dialog's positive button and then enable/disable it
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false

I'm new to Kotlin, I've been struggling to make this code save user input data to the hashMapOf and returning it when it loops again to choose a band

So the idea is you can check your favorite song for a certain band, by choosing the band name but the problem is I can't get it to take user input and return the info when it loops back again so the user can check his new added songs later.
import java.util.*
fun main(args: Array<String>) {
fun again() {
val favSongs = hashMapOf(
"metallica" to "The unforgiven III",
"disturbed" to "The vengeful one",
"godsmack" to "Running blind",
"lambofgod" to "The duke",
"21savage" to "A lot"
)
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n" +
"1.Metallica\n2.Disturbed\n3.Godsmack\n4.Lambofgod\n5.21savage\nEnter Here Please:"
)
val bandName = readln()
when (val bandNameLower: String = bandName.lowercase(Locale.getDefault())) {
"metallica" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"disturbed" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"godsmack" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"lambofgod" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"21savage" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
else -> println("Enter a valid band name!")
}
println("Do you want to check another favorite song? (1 for Yes\n0 for No\n+ to add new favorite band & song)")
val anotherSong = readln()
anotherSong.toString()
when (anotherSong) {
"1" -> again()
"0" -> println("Come back soon!")
}
if (anotherSong == "+") {
println("Enter the name of the band:")
val newBandName: String = readln()
println("What's your favorite song for $newBandName")
val newFavSong: String = readln()
favSongs["$newBandName"] = "$newFavSong"
again()
}
}
again()
}
As #cactustictacs mentioned, you need to have your favSongs map live outside of again() so that it's not reset each time you call the function.
Also, in order for the add a song flow to work, you need to tweak a few things. I updated the code here (with some comments that'll hopefully help):
fun main() {
// I moved this out of the again() function, plus changed it to Kotlin's MutableMap class
val favSongs = mutableMapOf(
"Metallica" to "The unforgiven III",
"Disturbed" to "The vengeful one",
"Godsmack" to "Running blind",
"LambofGod" to "The duke",
"21Savage" to "A lot"
)
fun again() {
// This now programmatically displays all your artists and songs rather than having them hard-coded.
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n" +
favSongs.keys
.mapIndexed { index, bandName -> "${index + 1}. $bandName" }
.joinToString("\n")
)
// The previous toString() call didn't change the original value so I removed it.
// Plus, bandName is already a string so no need to convert.
val bandName = readln()
// I took this approach since we're always doing the same thing (get song by band name)
// Also, this allows the entry to be upper or lower case, we don't care.
val favoriteSong = favSongs.entries.firstOrNull { (band, _) ->
band.equals(bandName, ignoreCase = true)
}?.value
if (favoriteSong != null) {
println("Your favorite song of $bandName is: $favoriteSong")
} else {
println("Enter a valid band name!")
}
println("Do you want to check another favorite song?\n(1 for Yes, 0 for No, + to add new favorite band & song)")
val anotherSong = readln()
// The "+" scenario can be included in the when { ... } block without issue.
when (anotherSong) {
"1" -> again()
"0" -> println("Come back soon!")
"+" -> {
println("Enter the name of the band:")
val newBandName: String = readln()
println("What's your favorite song for $newBandName")
val newFavSong: String = readln()
favSongs[newBandName] = newFavSong
again()
}
}
}
again()
}
(This is longer than I thought because I started running into the complications of what you're doing - I know you're new and I hope this isn't overwhelming, but there are some tricky things that need to be handled!)
Ok so here's one way you can organise the looping:
import java.util.Locale
val favSongs = mutableMapOf(
"metallica" to "The unforgiven III",
"disturbed" to "The vengeful one",
"godsmack" to "Running blind",
"lambofgod" to "The duke",
"21savage" to "A lot"
)
fun main() {
var option: String
// loop until the user types "0" in the options
do {
displayFav()
// show the options menu, and get the result
option = showOptions()
// if the user typed "+" we need to let them add a new fav before we loop
if (option == "+") addNewFav()
} while (option != "0")
// You can print this message when you leave the loop
println("Come back soon!")
}
fun displayFav() {
println(
"Choose a band to display your favorite song of it:\n(please enter the name of the band)\n" +
"1.Metallica\n2.Disturbed\n3.Godsmack\n4.Lambofgod\n5.21savage\nEnter Here Please:"
)
val bandName = readln()
when (val bandNameLower: String = bandName.lowercase(Locale.getDefault())) {
"metallica" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"disturbed" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"godsmack" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"lambofgod" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
"21savage" -> println("Your favorite song of $bandName is: " + favSongs[bandNameLower])
else -> println("Enter a valid band name!")
}
}
fun showOptions(): String {
println("Do you want to check another favorite song?")
println("(1 for Yes\n0 for No\n+ to add new favorite band & song)")
return readln()
}
fun addNewFav() {
println("Enter the name of the band: ")
val newBandName: String = readln()
println("What's your favorite song for $newBandName: ")
val newFavSong: String = readln()
favSongs[newBandName] = newFavSong
}
The while loop in main allows the program to keep running until you hit some condition, like the user entering a "quit" command in this case. So the loop basically gets their input, and checks what they typed at the end. If it's "0" it quits - anything else and it loops again (i.e. that's the default, as if they typed "1")
I've broken out the logic into a few different functions, so it's easier to look at main and see the big picture of how it's running. The details for each task are in the different functions, and you can tweak what's happening in each of those without worrying about the others.
And as an example of that - your code doesn't exactly work right now, because you're hardcoding your songs menu, and also the list of names it'll accept! Even though you can add songs to the map, you're not making use of the map when it comes to displaying bands or checking for valid names.
Here's how you can do that:
fun displayFav() {
println("Choose a band to display your favorite song of it:\n(please enter the name of the band)\n")
favSongs.keys
.mapIndexed { index, bandName -> "${index + 1}. $bandName\n" }
.forEach { println(it) }
val bandName = readln()
val favSong = favSongs[bandName]
if (favSong != null) println("Your favorite song of $bandName is: $favSong")
else println("Enter a valid band name!")
}
So now, we're taking the keys of the map (the band names) and using mapIndexed to transform them (along with their index in the map) into a numbered list of items. And we can take what the user typed in, try to fetch it from the map, and if that band exists we get the song - otherwise we get null and tell them to enter a valid name.
But here's the problem - you're using lowercase to create a case-insensitive lookup, right? That's fine, but you can't use the band names as keys in the table, and have them lowercase (for lookups) and correctly capitalised (for display). When the user adds their own band, you can lowercase the name to store it, but then you lose the formatting.
You have two options:
store the lowercase name as the key, mapped to a data structure (like a basic data class) that contains the normally formatted name (for display), and the fav song
store the normally formatted name as the key, mapped to the fav song, but look up a band by iterating over each entry in the map, lowercasing its key and comparing that to the user's lowercased input
I think the first option is better, but this is getting long enough and going off into all these other tangent concepts already, so I'll just do the "iterate over entries" thing so there aren't too many changes:
fun displayFav() {
println("Choose a band to display your favorite song of it:\n(please enter the name of the band)\n")
favSongs.keys
.mapIndexed { index, bandName -> "${index + 1}. $bandName\n" }
.forEach { println(it) }
val bandName = readln().lowercase(Locale.getDefault())
val favSong = favSongs.entries
.firstOrNull { (k, _) -> k.lowercase(Locale.getDefault()) == bandName}
?.value
if (favSong != null) println("Your favorite song of $bandName is: $favSong")
else println("Enter a valid band name!")
}
Now you can just store the band names correctly, and when it comes to the lookup comparison, that's when everything's lowercased. Not the most efficient way to do things (and using data classes gives you the option to easily add more data if you want, say an album name) but for a small map it doesn't really matter.
I wouldn't handle the user options input exactly like that either, but this is enough as it is!

Unable to replace string inside a String in Kotlin

I am trying to replace a few sub strings inside a string. But my code doesn't seem to work.
val listOfMaleWords = listOf(" him", " he", " his")
val listOfFemaleWords = listOf(" her", " she", " her")
fun modifyIdeaForGender(rawIdea : String, desiredGender : String): String {
var theRawIdea = rawIdea
if (desiredGender == "FEMALE") {
println("desired gender is FEMALE")
listOfMaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfFemaleWords[index])
}
} else {
println("desired gender is MALE")
listOfFemaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfMaleWords[index])
}
}
return theRawIdea
}
fun main() {
var sampleString : String = "Tell him, he is special"
println(modifyIdeaForGender(sampleString, "FEMALE"))
}
Expected Output :
"Tell her, she is special"
Current Output :
"Tell him, he is special" // no change
Whats wrong with my code? The current output doesn't replace the string characters at all.
replace returns a new String that you are discarding immediately. It does not mutate theRawIdea itself, so you should assign it back to theRawIdea yourself. For example:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
Though this would modify theRawIdea as you desire, it wouldn't replace the pronouns correctly. Once it replaces the "him"s with "her"s, it would try to replace the "he"s with "she"s. But note that "he" a substring of "her"! So this would produce:
Tell sher, she is special
This could be fixed by reordering the lists, putting the "he"-"she" pair first, or by using regex, adding \b word boundary anchors around the words:
// note that you should not have spaces before the words if you decide to use \b
val listOfMaleWords = listOf("him", "he", "his")
val listOfFemaleWords = listOf("her", "she", "her")
...
theRawIdea = theRawIdea.replace("\\b$element\\b".toRegex(), listOfFemaleWords[index])
Note that this doesn't account for capitalisation or the fact that changing from female gender pronouns to male ones is inherently broken. Your current code would change all her to him. It would require some more complicated natural language processing to accurately do this task in general.
Taking all that into account, I've rewritten your code with zip:
fun modifyMaleIdeaToFemaleGender(rawIdea : String): String {
var theRawIdea = rawIdea
// if you really want to do the broken female to male case, then this would be
// listOfFemaleWords zip listOfMaleWords
// and the loop below can stay the same
val zipped = listOfMaleWords zip listOfFemaleWords
zipped.forEach { (target, replacement) ->
theRawIdea = theRawIdea.replace("\\b$target\\b".toRegex(), replacement)
}
return theRawIdea
}
You can also use fold to avoid reassigning theRawIdea:
fun modifyIdeaToFemaleGender(rawIdea : String): String {
val zipped = listOfMaleWords zip listOfFemaleWords
return zipped.fold(rawIdea) { acc, (target, replacement) ->
acc.replace("\\b$target\\b".toRegex(), replacement)
}
}
Your code assumes that the replace() method performs an in-place mutation of the string. However, the string with the replaced values are returned by the replace(). So you need to change your code to contain something like:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
To do this, you will have to use a conventional loop instead of listOfMaleWords.forEachIndexed style looping.

Jetpack compose how to animate multiple values

I have couple of Path elements in my Canvas and would like to do some complex animations with every one of the Path lines. I am not sure how to approach this. Let's take a look at a simple path.
val line1Path = Path()
line1Path.moveTo(maxTopLeftX, 0f) // top left
line1Path.lineTo(maxBottomLeftX, size.height) // bottom left
line1Path.lineTo(maxBottomLeftX+lineWidth, size.height) // bottom right
line1Path.lineTo(maxTopLeftX+lineWidth, 0f) // top right
Currently I am using updateTransition with animateFloat but this way if I have to make animations for every one of the points only for this Path I would have to have 8 variables just for this 1 object.
Also I would like to do more than just a single value to value animation so something like animateFloatAsState where there are keyframes and I can order my animations seems better for the job, but the issue again is I have to create 8 variables that hold every one of the line positions to just animate this object.
What will be the best way to approach this?
I have been having same issue for days. In my case I use a data class for input data, so I just added an Animatable variable with default initialization to my data class. Then launch it from coroutine scope forEaching every item.
Not sure if this is the correct way to approach such issue, but hope it helps!
Here you have an example of multiple values animated at the same time. A transition is used to orchestrate the value animations.
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val transition = updateTransition(targetState = isPressed, label = "")
val angle by transition.animateFloat(transitionSpec = {
tween(durationMillis = 180, easing = FastOutSlowInEasing)
}, label = ""){
when(it){
true -> 90f
false -> 0f
}
}
val x by transition.animateDp(transitionSpec = {
tween(durationMillis = 180, easing = FastOutSlowInEasing)
}, label = ""){
when(it){
true -> 85.dp
false -> 0.dp
}
}
Column(modifier = Modifier.fillMaxSize().background(Color(0xFFF0F8FF))
.padding(80.dp).wrapContentSize(align = Alignment.BottomCenter),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// THIS IS THE ANIMATED BOX
Box(Modifier.rotate(angle).offset(x = x)
.background(Color.Red)
.width(20.dp)
.height(150.dp)
)
Box(modifier = Modifier.clickable(interactionSource = interactionSource, indication = null) {}
.hoverable(interactionSource)
.focusable(interactionSource = interactionSource)
.size(100.dp).background(Color.Blue),
)
}

How to use putextra () in Kotin

I want to send data (number) from "edit text section" of Sub-Activity1 (users input a simple number)and receive in another Sub-activity2, and depending on the number I want to show different sets of text. I am a beginner and I am stuck where in Sub-Activity 2 as it returns error for val str where I want to receive and manipulate the number received from Sub-Activity 1 editText.
Sub-Activity 1 :
<Send & Open Sub-Activity2>
getResult.setOnClickListener {
val intent = Intent(this, subactivity::class.java)
val name: String = editTextID.getText().toString()
intent.putExtra(name:"editTextID",value:"7.0")
startActivity(intent)
This returns no error.
Sub-Activity 2: <Receive & Manipulate the text>
class subactivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_subactivity2)
val str =intent.getStringExtra("editTextID")
when str == 7.0 {
infoTextView.textview.text= "textIwannaShow"
}
}
Franz might be right about you wanting to pass a Double, but since you're passing it as a String and also naming your val str, I'll assume you do want to pass a string (that just happens to represent a number). In which case you need to compare to a string
if (str == "7.0") {
infoTextView.textview.text = "textIwannaShow"
}
or if you do want a when block
when(str) {
"7.0" -> infoTextView.textview.text = "textIwannaShow"
}
If you actually want to work with numbers you'll have to call toDouble() on your string at some point to convert it to one. toDoubleOrNull would be better if you're taking that number from a text input (in case the user doesn't enter a valid number), but you're not actually using the value taken from the EditText
In your Sub-Activity 2, you are receiving a String not an Integer.
So, you should change your code from this
val str =intent.getStringExtra("editTextID")
To this
val str =intent.getIntExtra("editTextID", 0)
Anyway, in the example you are passing 7.0 which is Double, so you probably need this instead of above code
val str =intent.getDoubleExtra("editTextID", 0.0)