I'm using variable shadowing and I have a something like
val selectedEntryIds: LiveData<Set<Long>>
get() = _selectedProductIds
private val _selectedProductIds = MutableLiveData<MutableSet<Long>>(mutableSetOf())
However I get an error saying type mismatch.
Use the variance annotation out like this:
val selectedEntryIds: LiveData<out Set<Long>>
get() = _selectedProductIds
To tell the compiler that the LiveData will only produce such a set. Check:
https://kotlinlang.org/docs/generics.html#declaration-site-variance
You may be able to do the following
val selectedEntryIds: LiveData<Set<Long>>
get() = _selectedProductIds as LiveData<Set<Long>>
private val _selectedProductIds = MutableLiveData<MutableSet<Long>>(mutableSetOf())
as to cast the MutableLiveData<..> into a regular LiveData<..> object.
Related
I have view model and I use live data in encapsulation, which one is recommended to use and why?
private val _licenseStatusFromWebService = MutableLiveData<String?>()
val licenseStatusFromWebService: LiveData<String?> = _licenseStatusFromWebService
private val _licenseStatusFromWebService = MutableLiveData<String?>()
val licenseStatusFromWebService: LiveData<String?>
get() = _licenseStatusFromWebService
It Does not matter which way you use it as long as the MutableLiveData you are referring to is a val and not a var, but if you are going to modify or reassign the MutableLiveData to something else the getter approach get() = will return the latest instance and equals approach = will return the initial instance.
Also, Kotlin internally builds a getter for every property you have so if you are choosing the equals approach = for the sole purpose of reducing code on production, it will amount to nothing.
I think using an object directly is recommended way in ViewModel
private val _licenseStatusFromWebService = MutableLiveData<String?>()
val licenseStatusFromWebService: LiveData<String?> = _licenseStatusFromWebService
because, I am using this approach in some of my projects
It`s just to encapsulate mutable LiveData from immutable. As in UI you should use already prepared data from ViewModel to avoid modifying it from the UI directly.
private val _licenseStatusFromWebService = MutableLiveData<String?>()
val licenseStatusFromWebService: LiveData<String?> = _licenseStatusFromWebService
I am trying to find the proper syntax to initialize "Map<,<List<List>>>" in kotlin. I am a bit new to kotlin. I have variables initialized in my class as this:
var jitterMs: Double = 0.0
The way I am trying to initialize is this:
val bandMap: Map<Int,<List<List<String>>>> = emptyMap()
It gives me an error that a proper getter or setter is expected, and I think that's because it just doesn't understand what I am trying to do. What is wrong with this syntax?
Thanks,
Edit: As Tenfour pointed out, I actually want a mutable map. This is what I have now as an error saying there is a type error:
val bandMap: MutableMap<Int,List<List<String>>> = emptyMap()
This is how you would do it:
val bandMap: Map<Int, List<List<String>>> = emptyMap()
For a MutableMap write the following:
val bandMap: MutableMap<Int, List<List<String>>> = mutableMapOf()
Or, for the most idiomatic way to make a mutableMap would be:
val bandMap = mutableMapOf<Int, List<List<String>>>()
val bandMap: Map<Int,List<List<String>>> = emptyMap()
This will work. You added extra < >
I have an array of feedback channels because (outside of question scope) in my ViewModel.
Now, I don't want to expose my MutableLiveData to outside my Viewmodel.
So, i make a private list of LiveData objects, but compiler complains of "Useless Cast"
private val _feedbackChannels = Array(10) { MutableLiveData<FeedbackEvent>() }
val feedbackChannels
get() = _feedbackChannels.map{
#Suppress("USELESS_CAST") // it is not useless as it no longer exposes the mutableLiveData
it as LiveData<*>
}
Why do I get USELESS_CAST warning?
Compiler doesn't realize you're doing it only to force implication of property type.
Just specify type explicitly and you'll be able to drop the cast entirely. You won't even have to use map, a simple toList() will do:
private val _feedbackChannels = Array(10) { MutableLiveData<FeedbackEvent>() }
val feedbackChannels : List<LiveData<FeedbackEvent>>
get() = _feedbackChannels.toList()
Clearly the compiler doesn't understand the point of the cast. In order to do this in a more explicit way and remove the costly map function, you can just upcast it like this:
private val _feedbackChannels = Array(10) { MutableLiveData<FeedbackEvent>() }
val feedbackChannels: Array<out LiveData<FeedbackEvent>>
get() = _feedbackChannels
Edit
If you wanted to expose a List specifically (avoid exposing a mutable array) then you should probably just create one in the first place:
private val _feedbackChannels = List(10) { MutableLiveData<FeedbackEvent>() }
val feedbackChannels: List<out LiveData<FeedbackEvent>>
get() = _feedbackChannels
I am stuck in finding a solution to cannot find method onLoginButtonClick or in general android data binding bugs I want to make authentication in my app by using binding and room so this is my code
register layout
android:text="#{register.emailEditTextRegisterContext}"
android:text="#{register.passwordEditTextRegisterContext}"
android:onClick="#={()-> register.onButtonLoginRegisterClick(register)}"
android:text="#{register.confirmPasswordTextContext}"
android:text="#{register.nameRegisterContext}"
login layout
android:text="#{login.emailEditTextContext}"
android:text="#{login.PasswordEditTextContext}"
android:onClick="#={() -> login.onLoginButtonClick(login)}"
registerViewModel
private val _nameRegisterContext = MutableLiveData<String>()
val NameRegisterContext: LiveData<String>
get() = _nameRegisterContext
private val _emailEditTextRegisterContext = MutableLiveData<String>()
val emailEditTextRegisterContext: LiveData<String>
get() = _emailEditTextRegisterContext
private val _passwordEditTextRegisterContext = MutableLiveData<String>()
val passwordEditTextRegisterContext: LiveData<String>
get() = _passwordEditTextRegisterContext
private val _confirmPasswordTextContext = MutableLiveData<String>()
val confirmPasswordTextContext: LiveData<String>
get() = _confirmPasswordTextContext
private val _registerCompleted = MutableLiveData<Boolean>()
val registerCompleted: LiveData<Boolean>
get() = _registerCompleted
fun onButtonLoginRegisterClick(view: View){
}
login layout
private val _emailedittextcontext = MutableLiveData<String>()
val emailEditTextContext: LiveData<String>
get() = _emailedittextcontext
private val _passwordedittextcontext = MutableLiveData<String>()
val PasswordEditTextContext: LiveData<String>
get() = _passwordedittextcontext
fun onLoginButtonClick(view : View){
onLogingin()
}
error(whenever I tried a solution it changes)
[kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors.
****/ data binding error ****msg:cannot find method onButtonLoginRegisterClick(com.example.android.trackmysleepquality.register.RegisterViewModel) in class com.example.android.trackmysleepquality.register.RegisterViewModel
file:C:\Users\mehmet\Desktop\andfun-kotlin-sleep-tracker-with-recyclerview\app\src\main\res\layout\register_layout.xml
loc:87:32 - 87:81
****\ data binding error ****
****/ data binding error ****msg:cannot find method onLoginButtonClick(com.example.android.trackmysleepquality.login.Login_ViewModel) in class com.example.android.trackmysleepquality.login.Login_ViewModel
file:C:\Users\mehmet\Desktop\andfun-kotlin-sleep-tracker-with-recyclerview\app\src\main\res\layout\login_layout.xml
loc:76:32 - 76:68
****\ data binding error ****
please help!!
Although answer posted by #Miller Go Dev corrects your code somewhat, it still doesn't satisfy the needs.
The correct way would be this,
android:onClick="#={(view) -> login.onLoginButtonClick(view)}"
android:onClick="#={(view)-> register.onButtonLoginRegisterClick(view)}"
//pass the view parameter to their respective method
Wrong:
android:onClick="#={() -> login.onLoginButtonClick(login)}"
android:onClick="#={()-> register.onButtonLoginRegisterClick(register)}"
You are passing ViewModel object to these functions, not View as you want
Right:
android:onClick="#={(view) -> login.onLoginButtonClick()}"
android:onClick="#={(view)-> register.onButtonLoginRegisterClick()}"
Ref:
Listener Binding in official doc
well, all I did to fix is to remove the = sign to make the data binding one-way
Wrong:
android:onClick="#={() -> login.onLoginButtonClick(login)}"
Right:
android:onClick="#{() -> login.onLoginButtonClick()}"
I understand the need for creating getter and setter points for LiveData in the ViewModel, but I'm looking to understand how the get() syntax works in Android.
ie:
val isRealtime: LiveData<Boolean>
get() = _isRealtime
private val _isRealtime = MutableLiveData<Boolean>()
get() is not related to Android.
val isRealtime: LiveData<Boolean>
get() = _isRealtime
Here, get() is overriding the automatically-generated Kotlin getter function for the isRealtime property. So, instead of returning its own value, it returns the value of _isRealtime.
Personally, I recommend simpler syntax:
private val _isRealtime = MutableLiveData<Boolean>()
val isRealtime: LiveData<Boolean> = _isRealtime
The objective of either of these is to keep the mutability private, so consumers of this class do not accidentally update the MutableLiveData themselves.
In Kotlin we have multiple ways of exposing live data from ViewModel to the view.
class MyViewModel: ViewModel() {
// Solution 1 - make MutableLiveData public
// This approach works, but this is a bad idea because
// view can modify the LiveData values
val liveDataA1 = MutableLiveData<State>()
// Solution 2 - let's make LiveData public (expose it instead of MutableLiveData)
// Now from view perspective this solution looks fine, bu we have a problem,
// because we need MutableLiveData within ViewModel to put/post new values to
// the stream (we can't post values to LiveData).
val liveDataA2 = MutableLiveData<State>() as LiveData<State>
// Let's capture our requirements:
// 1. We need to expose (immutable) LiveData to the view,
// so it cannot edit the data itself.
// 2. We need to access MutableLiveData from ViewModel to put/post new values.
// Now, let's consider few appropriate solutions
// Solution 3
// Let's name mutable live data using underscore prefix
private val _liveData3 = MutableLiveData<State>()
val liveData3 = _liveData3 as LiveData<State>
// Solution 4
// We can also perform casting by specifying type for a variable
// (we can do it because MutableLiveData extends LiveData)
private val _liveData4 = MutableLiveData<State>()
val liveData4: LiveData<State> = _liveData4
// Solution 5
// Starting from Kotlin 1.4-M.2 we can delegate call to another property
private val _liveData5 = MutableLiveData<State>()
val liveData5 by this::_liveData5
// Solution 6
// These above solutions work quite well, but we could do even better by
// defining custom asLiveData extension function.
private val _liveData6 = MutableLiveData<State>()
val liveData6 = _liveData6.asLiveData()
fun <T> MutableLiveData<T>.asLiveData() = this as LiveData<T>
// Amount of code is similar, but notice that this approach works much better
// with code completion.
// Solution 7 (IMO Best)
// We can also use alternative naming convention - use "mutableLiveData"
// as variable for mutable live data instead of using underscore prefix
private val mutableLiveData7 = MutableLiveData<State>()
val liveData7 = mutableLiveData7.asLiveData()
// BTW
// We could also expose getLiveData8() method, but liveData is a state not an action.
// Solution 9
// This does not create backing field for the property
// (more optimised but still Solution 7 is easier to use)
private val _liveData9 = MutableLiveData<State>()
val liveData9 get() = _liveData9 as LiveData<State>
}
I wrote a util function for this logic:
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import kotlin.reflect.KProperty
fun <T> immutable(data: MutableLiveData<T>): Immutable<T> {
return Immutable(data)
}
class Immutable<T>(private val data: MutableLiveData<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): LiveData<T> {
return data
}
}
Then you can use in any of your ViewModel as:
private val _counter: MutableLiveData<Int> = MutableLiveData()
val counter: LiveData<Int> by immutable(_counter)
or in short:
private val _counter = MutableLiveData<Int>()
val counter by immutable(_counter)