Je comprends le besoin de créer des points getter et setter pour LiveData dans le ViewModel, mais je Je cherche à comprendre comment la syntaxe get ()
fonctionne sous Android.
ie:
val isRealtime: LiveData<Boolean> get() = _isRealtime private val _isRealtime = MutableLiveData<Boolean>()
3 Réponses :
get ()
n'est pas lié à Android.
private val _isRealtime = MutableLiveData<Boolean>() val isRealtime: LiveData<Boolean> = _isRealtime
Ici, get ()
remplace le getter Kotlin généré automatiquement fonction pour la propriété isRealtime
. Donc, au lieu de renvoyer sa propre valeur, il renvoie la valeur de _isRealtime
.
Personnellement, je recommande une syntaxe plus simple:
val isRealtime: LiveData<Boolean> get() = _isRealtime
L'objectif de l'un ou l'autre de ces éléments est de garder la mutabilité privée, afin que les consommateurs de cette classe ne mettent pas accidentellement à jour le MutableLiveData
eux-mêmes.
Merci d'avoir partagé la syntaxe plus simple @CommonsWare! Il est bon de savoir que je n'ai pas besoin d'indiquer explicitement get ()
.
@AdamHurwitz: get ()
a ses utilisations, mais pour des scénarios plus complexes. Par exemple, un modèle dans Android consiste à créer des propriétés d'extension privées sur Intent
pour les extras ou sur Bundle
pour les arguments de fragment. Là, vous remplaceriez get ()
et set ()
sur les propriétés de l'extension pour acheminer vers les getters / setters appropriés (par exemple, getString () code > et
putString ()
sur un Bundle
). Pour un cas simple comme celui-ci, cependant, cela semble exagéré.
En outre, un avantage organisationnel de conserver get ()
est qu'il vous permet de définir toutes les variables publiques en haut de la classe ViewModel
.
J'ai écrit une fonction util pour cette logique:
private val _counter = MutableLiveData<Int>() val counter by immutable(_counter)
Ensuite, vous pouvez utiliser dans l'un de vos ViewModel comme:
private val _counter: MutableLiveData<Int> = MutableLiveData() val counter: LiveData<Int> by immutable(_counter)
ou en bref:
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 } }
Dans Kotlin, nous avons plusieurs façons d'exposer les données en direct de ViewModel à la vue.
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> }
J'aime vraiment la solution n ° 5. Malheureusement, je ne peux pas encore l'utiliser sur Android. BTW, je pense que this :: _ liveData5
peut en fait être simplement liveData5
Non, ça ne peut pas. D'une manière ou d'une autre, le this ::
est nécessaire
Vous avez déjà essayé de lire la documentation de Kotlin sur les fonctions?
Oui, mais ça fait quelques années. Je vais vérifier ça. Merci pour la pointe @MarcinOrlowski!