how to import an extension function in Kotlin? - kotlin

this is the function i'm trying to import https://developer.android.com/reference/kotlin/androidx/compose/ui/unit/Density#(androidx.compose.ui.unit.DpRect).toRect()
here is my code
val cardWidth = 180f.dp
val cardHeight = 270f.dp
val fullHeight = 296f.dp
val focusRect = DpRect(0f.dp, 0f.dp, cardWidth, fullHeight).toRect()
i have tried importing like
import androidx.compose.ui.unit.Density.toRect
import androidx.compose.ui.unit.toRect
import androidx.compose.ui.unit.DpRect.toRect
but non of them works.

That's an extension function inside a Density object, a member function - you need to run it within that context. You can't just import it and use it anywhere, because it's internal to that object. (See the comment in the first example here.)
So you need to do something like this:
val focusRect = Density(density = 2f).run {
DpRect(0f.dp, 0f.dp, cardWidth, fullHeight).toRect()
}
which, import limitations aside, makes sense - how can you convert dp values to px values if you haven't defined the density involved? You can see how it works in the source code:
fun DpRect.toRect(): Rect {
return Rect(
left.toPx(),
top.toPx(),
right.toPx(),
bottom.toPx()
)
}
fun Dp.toPx(): Float = value * density
where density is a property on the Density object, the one we passed in as a parameter. Basically you can create a Density and use it to perform conversions based on that density. The extension functions don't make sense outside of that context, right?

Related

Trying to get a when expression to show the result on the ui in kotlin

I have been stuck making this metric converter app for over a week I keep going back to the previous codelabs that explain how to make a tip calculator but when I try to apply the same method to this app that I'm trying to make it doesn't have any errors but the app crashes whenever I try to test it to see if I can get the functionality to work this is my last resort. I've tried to do when statements but I kept getting the error that certain branches would never be reached. if anyone can point me in the right direction as to what I'm doing wrong or what's missing from my code. I'm all ears.
package com.example.metricconversion
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.metricconversion.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
setContentView(binding.root)
binding.conversionTo.setOnClickListener { convertMetric() }
}
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// val grainToQtr = R.id.Grain and R.id.quarter1
//val grainToSt= R.id.Grain and R.id.quarter1
//val grainToLb = R.id.Grain and R.id. pound1
//val grainToTon= R.id.Grain and R.id.Ton1
val st = R.id.Grain and R.id.stone1
// code to convert grain to the other options
//code to convert grain to the other options
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ounce1 -> {
amountIQ / 437.5
}
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.quarter1 -> amountIQ * 0.000005714
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.pound1 -> amountIQ * 0.0001429
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ton1 -> amountIQ / 1.4e+7
}
when (selectedId and selectedId2) {
st -> {
amountIQ * 0.0000102
}
}
binding.metricConverted.text=getString(R.string.end_result,convertMetric())
}
}
Problems with your code:
Using and to check two values for equality at the same time is not viable. The and function does a bitwise operation to merge the two numbers together. There are many possible inputs that could merge to the same solution, so you will get false positives. Instead you could use the to infix function to combined the two numbers into a Pair wrapper object so they are both preserved for the comparison.
You have a series of individual when statements, each with only a single condition. It doesn't make sense to use a when statement with a single condition. Usually a single condition would be represented with an if statement instead of when statement. But I'm guessing you didn't mean for these to be separate when statements, based on what I'm seeing.
Your when statements are not actually doing anything. Their branches resolve to a number, but you're not doing anything with that number (not assigning it to a variable or logging it or showing it in the UI, etc.). So they are useless. Your statement at the bottom isn't getting the result of any of the when statements, but is instead recursively calling the same function again. This function doesn't return anything, so that's useless. Even if it did return a number, the recursive call will create an infinite loop, resulting in a stack overflow.
So first, to just fix your code (we can discuss a better way of doing it later):
We replace and with to.
We merge all the when statements into a single statement.
We store the result of the when statement in a variable and use that variable to set the text in the last line of the function. When you use a when statement with a subject (something in parentheses that is compared for each branch) or to get a result, you have to cover every possible case, so an else branch must also be added.
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// code to convert grain to the other options
val result = when (selectedId to selectedId2) {
R.id.Grain to R.id.Ounce1 -> amountIQ / 437.5
R.id.Grain to R.id.quarter1 -> amountIQ * 0.000005714
R.id.Grain to R.id.pound1 -> amountIQ * 0.0001429
R.id.Grain to R.id.Ton1 -> amountIQ / 1.4e+7
R.id.Grain to R.id.stone1 -> amountIQ * 0.0000102
else -> error("Unsupported selection pair")
}
binding.metricConverted.text = getString(R.string.end_result, result.toString())
}
Above, the error() call will crash your app. You need to make sure you cover every possible combination that could occur. During development, this is suitable, but for production you might want to change the behavior so it shows an error message in the UI of the app and doesn't crash.
Now, regarding the overall design, it is quite fragile because you have UI layout details so tightly coupled to your app's behavior. All these formula calculations should probably be defined in a separate class, possibly an Enum class. You could create a Map in your Activity file that links the UI elements to the behavior class(es), and then in your when statement, you could use the UI elements to pull the associated behavior from the map. This would be more maintainable, and make it easier for you to avoid forgetting something as you add/modify functionality. I say this just to get you thinking about it, but it's probably too much for a beginner project right now. I don't have time to explain in detail how I would do all of that for your case.

Can't Parse a double from user input Kotlin

I'm trying to learn and create a basic sales tax app (inputs from item price and tax). In order to do this, I need to use a float or double for the decimals. I am very new to kotlin and don't know much, so I do not understand how I can get my num1 to parseDouble, I can clearly do it with the Integer data type with no errors. I've also tried toDouble which is giving me errors. Any help would be appreciated, thank you!
val calcButton = findViewById<Button>(R.id.calcButton)
calcButton.setOnClickListener {
var textPrice: EditText = findViewById(R.id.textPrice)
var textTax: EditText = findViewById(R.id.textTax)
var textTotal: EditText = findViewById(R.id.textTotal)
// error here: "Unresolved reference: parseDouble"
var num1 = Double.parseDouble(textTax.getText().toString());
// "parseInt" is working, however
var num2 = Integer.parseInt(textTax.getText().toString());
}
Try:
textTax.getText().toString().toDouble();
The reason you can't use Double.parseDouble is because Double in kotlin is not exactly the same as Double in Java, therefore, just calling Double will call kotlin's version, which doesn't have parseDouble static method.
If you want to use the Double from java.lang, you must specify the full package name:
java.lang.Double.parseDouble(textTax.getText().toString());
Or you can also do:
import java.lang.Double.*;
parseDouble(textTax.getText().toString());
I would suggest just sticking with Kotlin's versions, as they usually result in shorter code.

How do I add multiple copies of the same String to an existing ArrayList?

I have an ArrayList<String>. I want to add n copies of a new String to it.
I've Googled generally and searched on StackOverflow. I've looked at the documentation.
Surely there's a better way than doing a loop?
I was hoping for something like:
myArray.addAll (ArrayList<String>(count: 10, value: "123"))
You can initialize a List with a given size n and an initializer function like this:
fun main() {
val n = 10
val defaultList = List(n) { it -> "default" } // you can leave "it ->" here
println(defaultList)
}
This piece of code then outputs
[default, default, default, default, default, default, default, default, default, default]
If you want to intialize an Array<String> directly without using a List as intermediate, you can do
val defaultArray: Array<String> = Array(n) { "default" }
println(defaultArray.contentToString())
in the main and get the same output (even without the it ->, which, indeed, isn't necessary in this case).

How do I distinguish a global variable from a local one with the same name in Kotlin?

I'm learning Kotlin, and in my project I have something like the following
Utils.kt:
var weightInKilos = 100.0
//should multiply the above var
fun doSomething(multiplier: Double, weightInKilos: Double) {
weightInKilos = weightInKilos * multiplier
}
print(doSomething(4.2, weightInKilos))
This would be the entire file (it's not part of an object,) so I can't use the this keyword. I know I could just rename one of them but is there some kind of identifier I can use to distinguish the two vars so the code prints 420?
Use the package name as the identifier. If the enclosing package for Utils.kt file is com.example, you would use com.example.weightInKilos = weightInKilos * multiplier.
Thank you Android Studio autocomplete.

0xFF0000FF An integer literal does not conform to the expected type kotlin.Int

Why doesn't this work:
var color: Int = 0xFF0000FF
and why do I have to call toInt()
var color: Int = 0xFF0000FF.toInt()
This is a bug in the compiler, feel free to vote / watch it: https://youtrack.jetbrains.com/issue/KT-2780
I am using boolean an in my case I was using this way with same error
var shouldShowOnBoarding by remember { mutableStateOf(true) }
Previous import was
import androidx.compose.runtime.Composable
Then I changed it to
import androidx.compose.runtime.*