Android studio is giving me a squiggly saying that I should/could turn this into a lambda. I'm just getting back into my android.
popup.setOnMenuItemClickListener(object : PopupMenu.OnMenuItemClickListener {
override fun onMenuItemClick(item: MenuItem): Boolean {
if (item.itemId === R.id.action_vitals) {
val vitalsIntent = Intent(this#DashboardActivity, VitalsActivity::class.java)
vitalsIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(vitalsIntent)
}
if (item.itemId === R.id.action_devices) {
val devicesIntent = Intent(this#DashboardActivity, DevicesActivity::class.java)
devicesIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(devicesIntent)
}
return false
}
})
This is the squiggly "object : PopupMenu.OnMenuItemClickListener"
There are a couple of comments and answers suggesting to use alt + enter in IntelliJ/Android Studio and that will handle converting your code into a Lambda for you. However, it might be worth covering why it is suggesting that to you.
A lambda is a function that isn't declared e.g. fun someFunction() but instead is immediately passed as a parameter to another function. This lambda will then be executed by some other code elsewhere in the app. Essentially a Lambda is a shorthand function e.g:
val lambda: () -> Unit = {
// Some code could go inside this Lambda here
}
An important concept for you here is a SAM (Single Abstract Method) type. This simply refers to an interface that defines a single abstract function that needs to be implemented. In your example PopupMenu.OnMenuItemClickListener is a Java interface that has a single abstract method void onMenuItemClick(MenuItem item). SAM types can be written in shorthand with the body of the lambda becoming the body of abstract function.
You've correctly written this as an anonymous object which is fine; but it could be written more concisely with a lambda which Android studio is suggesting. Another helpful tidbit, in Kotlin if a function or lambda is the sole or final parameter in a list of parameters it can be moved outside of the braces of the function or they can be removed entirely.
So your code will have been converted to something like this:
enterpopup.setOnMenuItemClickListener {
if (item.itemId === R.id.action_vitals) {
val vitalsIntent = Intent(this#DashboardActivity, VitalsActivity::class.java)
vitalsIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(vitalsIntent)
}
if (item.itemId === R.id.action_devices) {
val devicesIntent = Intent(this#DashboardActivity, DevicesActivity::class.java)
devicesIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
startActivity(devicesIntent)
}
return false
}
You can use Alt+Enter to show a list of "suggestions" (called inspections in JetBrains language). Selecting the suggestion will perform the conversion automatically for you.
IntelliJ 2019.2 brings an improvement to this feature (which should come to Android Studio soon), showing more info about the top suggestion and adding Alt+Shift+Enter as a shortcut to directly apply it (bypassing the list)
https://www.jetbrains.com/idea/whatsnew/#v2019-2
Related
In the code below, i'd like to generalize it so I instead of viewBinding.editText.text and viewModel.property.price can use the same method for e.g viewBinding.secondEditText.text and viewModel.property.income.
I'm thinking exchanging viewBinding.editText.text for a variable defined in the primary constructor, but then I'd need the variable to contain a reference to viewBinding.editText.text/viewBinding.secondEditText.text etc. instead of containing a value.
Is this possible? I've looked at lengths for this but can't find anything useful.
fun updateProperty() {
//... other irrelevant code
if (viewBinding.editText.text.toString() != "") {
viewModel.property.price = viewBinding.editText.text.toString().toDouble()
}
//... other irrelevant code
}
You can pass parameters into a function, yeah!
This is the easy one:
fun updateProperty(editText: EditText) {
val contents = editText.text.toString()
}
simple enough, you just pass in whatever instance of an EditText and the function does something with it.
If you're just using objects with setters and getters, you can just define the type you're going to be using and pass them in. Depending on what viewmodel.property is, you might be able to pass that in as well, and access price and income on it. Maybe use an interface or a sealed class if there are other types you want to use - they need some commonality if you're going to be using a generalised function that works with them all.
Properties are a bit tricker - assuming viewmodel.property contains a var price: Double, and you didn't want to pass in property itself, just a Double that exists somewhere, you can do it like this:
import kotlin.reflect.KMutableProperty0
var wow: Double = 1.2
fun main() {
println(wow)
setVar(::wow, 6.9)
println(wow)
}
fun setVar(variable: KMutableProperty0<Double>, value: Double) {
variable.set(value)
}
>> 1.2
>> 6.9
(see Property references if you're not familiar with the :: syntax)
KMutableProperty0 represents a reference to a mutable property (a var) which doesn't have any receivers - just a basic var. And don't worry about the reflect import, this is basic reflection stuff like function references, it's part of the base Kotlin install
Yes, method parameters can also be references to classes or interfaces. And method parameters can also be references to other methods/functions/lambdas.
If you are dealing with cases that are hard to generalize, consider using some kind of inversion of control (function as parameter or lambda).
You add a lambda parameter to your updateProperty function
fun updateProperty(onUpdate: (viewBinding: YourViewBindingType, viewModel: YourViewModelType) -> Unit) {
//... other irrelevant code
// here you just call the lambda, with any parameters that might be useful 'on the other side'
onUpdate(viewBinding, viewModel)
//... other irrelevant code
}
Elsewhere in code - case 1:
updateProperty() { viewBinding, viewModel ->
if (viewBinding.editText.text.toString() != "") {
viewModel.property.price = viewBinding.editText.text.toString().toDouble()
}
}
Elsewhere in code - case 2:
updateProperty() { viewBinding, viewModel ->
if (viewBinding.secondEditText.text.toString() != "") {
viewModel.property.income = viewBinding.secondEditText.text.toString().toDouble()
}
}
Elsewhere in code - case 3:
updateProperty() { viewBinding, viewModel ->
// I am a totally different case, because I have to update two properties at once!
viewModel.property.somethingElse1 = viewBinding.thirdEditText.text.toString().toBoolean()
viewModel.property.somethingElse2 = viewBinding.fourthEditText.text
.toString().replaceAll("[- ]*", "").toInt()
}
You could then go even further and define a function for the first 2 cases, since those 2 can be generalized, and then call it inside the lambda (or even pass it as the lambda), which would save you some amount of code, if you call updateProperty() in many places in your code or simply define a simple function for each of them, and call that instead, like this
fun updatePrice() = updateProperty() { viewBinding, viewModel ->
if (viewBinding.editText.text.toString() != "") {
viewModel.property.price = viewBinding.editText.text.toString().toDouble()
}
}
fun updateIncome() = updateProperty() { viewBinding, viewModel ->
if (viewBinding.secondEditText.text.toString() != "") {
viewModel.property.income = viewBinding.secondEditText.text.toString().toDouble()
}
}
Then elsewhere in code you just call it in a really simple way
updatePrice()
updateIncome()
This question already has an answer here:
I don't understand the argument passing of the lambda expression in the listener
(1 answer)
Closed 1 year ago.
I found on github these lines of code and spend more than hour trying to understand.
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_fragment_tasks, menu)
val searchItem = menu.findItem(R.id.action_search)
val searchView = searchItem.actionView as SearchView
searchView.onQueryTextChanged {
viewModel.searchQuery.value = it
}
}
and this is the part im having hard time trying to understand:
inline fun SearchView.onQueryTextChanged(crossinline listener: (String) -> Unit) {
this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
listener(newText.orEmpty())
return true
}
})
}
I literally don't understand almost a single word.
inline crossline aside, can you explain me how listener works there.
P.S Im noob in kotlin, my first language is Java
I am learning Kotlin myself and thought I can help with the code snippet above.
SearchView.OnQueryTextListener is an interface with onQueryTextChange and onQueryTextSubmit methods - reference - https://developer.android.com/reference/kotlin/android/widget/SearchView.OnQueryTextListener
SearchView.onQueryTextChanged function looks like an extension function on SearchView, which accepts a lambda named listener.
The listener lambda itself is a function which takes a string as param and returns Unit ie returns nothing. That means the function will do some side-effect eg IO, update some state etc.
Since onQueryTextChanged is an extension function, you get the reference to the object on which this function will be called as this in the function body
object : SearchView.OnQueryTextListener part is basically an object implementing the SearchView.OnQueryTextListener interface with both the methods from the interface overridden.
The object in above step is passed as param to setOnQueryTextListener method.
At the caller site, i.e.
searchView.onQueryTextChanged {
viewModel.searchQuery.value = it
}
Please note the it value is implicit param to a lambda, you can rewrite it to make it more readable as well,
searchView.onQueryTextChanged { newSearchQuery ->
viewModel.searchQuery.value = newSearchQuery
}
the listener lambda here is a function which takes a string and sets the value to viewModel.searchQuery.value.
The inline keyword basically replaces function call with function body directly at compile time, kind of what inline variable refactoring that can be done, although the compiler does it at compile time - kind of an optimization.
Been trying to duplicate an iOS (Swift) solution in Android (Kotlin). It's fairly straight forward, but I'm not sure if I'm on the right track, or if I need to change up my Kotlin code entirely. (Note this solution is working in Swift)
Here is what's going on in Swift:
enum CarID: String, Codable {
case carHeader
case carView
case dataView
var viewControllerType: UIViewController.Type {
switch self {
case .carView: return CarViewController.self
case .carHeader: return CarHeaderViewController.self
case .dataView: return DataViewController.self
}
}
}
Now my attempt to match it in Kotlin:
enum class ModuleID(val type: String) {
CAR_HEADER("carHeader"),
CAR_VIEW("carView"),
DATA_VIEW("dataView");
val activityType: AppCompatActivity // Wrong return type?
get() = when(this) {
CAR_HEADER -> **CarHeader** // Error here
CAR_VIEW -> CarView
DATA_VIEW -> DataView
}
}
Note I am simply trying to use ActivityViewTypes, just as iOS is using the viewControllerType. The behavior should be near exact.
The error I am seeing reads: "Classifier 'CarHeader' does not have a companion object, and thus must be initialized here."
I attempted to create said companion object, but it just didn't seem necessary, so I backed off. Any suggestions are welcomed! I've also considered looking into a Sealed Class, but not sure if that is necessary here, either.
I have also not addressed the 'Codable - if someone wants to delve into that, that is fine as well.
Thanks!
What you want is a Type, and not an instance, so you'd probably want to return a class, so in Java terms:
UIViewController.Type ==> `Class<? extends Activity>`
With that in mind, following should work:
val activityType: Class<out Activity>
get() = when(this) {
CAR_HEADER -> CarHeader::class.java
CAR_VIEW -> CarView::class.java
DATA_VIEW -> DataView::class.java
}
}
Which you can even use like
startActivity(Intent(this, module.activityType))
I've been writing in Kotlin for a while, and I get used to use next pattern:
variable.addSomething(object: TargetType() { ...code... })
or
var variable = object: TargetType() { ...code... }
(if i'm not missing something)
Is it possible to somehow use this pattern in Swift ? And how is it called ? :)
Edit:
What I actually want to do - to store preconfigured RxSwift.SingleEvent in a let / var inside object and reuse it later multiple times.
In code, as I imagine, it should look like that:
private var observer = SingleEvent<Response>(ok_callback, error_callback) {
override success(el: Element) {
ok_callback(el)
super.success(el)
}
override error(er: Error) {
self.onErrorRetry(er, callback)
}
}
And if retry after some magic works - simply call my callback and return back :)
Its seems to be trailing closure. Adapted from Swift programming language - Closures:
If you need to pass a closure expression to a function as the
function’s final argument and the closure expression is long, it can
be useful to write it as a trailing closure instead. A trailing
closure is written after the function call’s parentheses, even though
it is still an argument to the function. When you use the trailing
closure syntax, you don’t write the argument label for the closure as
part of the function call.
Let's code it:
Simply, all you have to do is just to create a function which its last argument is a closure:
func doSomething(firstParameter: Any, closure: () -> Void) { }
thus you could call it as:
doSomething(firstParameter: "whatever") {
// ...
}
Nothing special goes here, it is a cool feature from the Swift language to "trail" closure parameter if its the last one in the function signature.
In case of initialization, it is almost the same:
struct MyObject {
init(firstParameter: Any, closure: () -> Void) { }
}
let variable = MyObject(firstParameter: "whatever") { }
Certainly, this pattern is followed by many other functions in the language, but here is an example of the merge method for the Dictionary, you could recognize how you could type it in more than one way as mentioned in the answers of: Map Dictionary Keys to add values - Swift.
Update:
If you are aiming to use it as constant/variable -to be passed for a function for example-, you could do it like this:
let variable: (String) -> Void = { name in
print("The name is: \(name)!")
}
At this point, variable type is (String) -> Void which means that its a constant that could be passed somewhere else; Consider the following method:
func doSomething(closure: (String) -> Void) {
closure("Nikita")
}
Because doSomething takes a parameter of type (String) -> Void, you could do:
doSomething(closure: variable) // The name is: Nikita!
instead of calling it as:
doSomething { name in
print("The name is: \(name)!")
}
which prevents the boilerplate code.
I have a Kotlin class (The problem was simplified to have a basic example), in it there is a method testedMethod() I want to test interactions on.
I want to make sure the correct parameter is passed to anotherMethod(), my problem is that the parameter is not a value but a lambda function.
A simple solution in Kotlin is what I am looking for, it may or may not be based on this solution for java.
class TestedClass {
fun testedMethod() {
anotherMethod({ passedMethod() })
}
fun passedMethod() {}
fun anotherMethod(lambdaParameter: () -> Unit) {
// Does something with lambdaParameter
}
}
This is the solution I ended up using.
Basically you create a spy of the tested class to be able to verify its own method, and you use argumentCaptor to capture the passed lambda and be able to invoke what is in it.
import com.nhaarman.mockito_kotlin.argumentCaptor
import com.nhaarman.mockito_kotlin.spy
import com.nhaarman.mockito_kotlin.verify
#Test
fun lambdaParameterPassedIsCorrect() {
val testedClass = TestedClass()
val spyOfTestedClass = spy(testedClass)
val captor = argumentCaptor<() -> Unit>()
spyOfTestedClass.testedMethod()
verify(spyOfTestedClass).anotherMethod(captor.capture())
// Invoke the function passed as a parameter
// and then verify that the expected method was called
val function = captor.firstValue
function.invoke()
verify(spyOfTestedClass).passedMethod()
}
I would still be interested in a simpler solution.
You can use mockito_kotlin library, which have the function "verify".
verify(TestedClass, times(1)).anotherMethod(eq(passedMethod))
Which verify that the method "anotherMethod" of class "TestedClass" invoked once with the parameter equal to "passedMethod"