Je veux utiliser LiveData avec Kotlin et avoir des valeurs qui ne devraient pas être nulles. Comment gérez-vous cela? Peut-être un wrapper autour de LiveData? Recherche de bons modèles ici .. À titre d'exemple:
class NetworkDefinitionProvider : MutableLiveData<NetworkDefinition>() {
val allDefinitions = mutableListOf(RinkebyNetworkDefinition(), MainnetNetworkDefinition(), RopstenNetworkDefinition())
init {
value = allDefinitions.first()
}
fun setCurrent(value: NetworkDefinition) {
setValue(value)
}
}
Je sais que la valeur ne sera pas nulle lors de l'accès - mais je devrai toujours vérifier la valeur nulle ou avoir ces vilains !!.
J'ai créé une propriété d'extension. Ce n'est pas super joli, mais c'est est assez simple.
val <T> LiveData<T>.valueNN
get() = this.value!!
Usage
spinner.loading = myLiveData.valueNN.homeState.loading
Je ne suis pas convaincu d'avoir ajouté "NN" comme bonne convention de dénomination, mais cela dépasse la portée de la question :)
J'améliore peu réponse The Lucky Coder. Cette implémentation ne peut pas accepter du tout les valeurs nulles.
class NonNullMutableLiveData<T: Any>(initValue: T): MutableLiveData<T>() {
init {
value = initValue
}
override fun getValue(): T {
return super.getValue()!!
}
override fun setValue(value: T) {
super.setValue(value)
}
fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
observe(owner, Observer<T> { t -> body(t!!) })
}
override fun postValue(value: T) {
super.postValue(value)
}
}
Je ne sais pas si c'est la meilleure solution mais c'est ce que j'ai trouvé et ce que j'utilise:
class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {
override fun getValue(): T = super.getValue() ?: defaultValue
fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
observe(owner, Observer<T> {
body(it ?: defaultValue)
})
}
}
Création du champ:
val string = NonNullLiveData("")
Et en l'observant:
viewModel.string.observe(this) {
// Do someting with the data
}
Vous pouvez créer une extension pour LifecycleOwner
fun <T> LifecycleOwner.observe(liveData: LiveData<T?>, lambda: (T) -> Unit) {
liveData.observe(this, Observer { if (it != null) lambda(it) })
}
puis dans votre fragment/activité
observe(liveData) { ... }
Tu peux le faire
normalLiveData
.nonNull()
.observe(this, { result ->
// result is non null now
})
Il y a un article à ce sujet. https://medium.com/@henrytao/nonnull-livedata-with-kotlin-extension-26963ffd03
J'ai vraiment aimé The Lucky Coder 's answer. En ce qui concerne le danger d'essayer de définir une valeur nulle, je pense que nous devrions lever une exception pour cela (similaire à ce que David Whitman pointait):
class NonNullLiveData<T>(private val defaultValue: T) : MutableLiveData<T>() {
override fun getValue(): T = super.getValue() ?: defaultValue
override fun setValue(value: T) {
if(value != null) {
super.setValue(value)
}
else throw IllegalArgumentException("Cannot set a null value to this Type. Use normal MutableLiveData instead for that.")
}
override fun postValue(value: T) {
if(value != null) {
super.postValue(value)
}
else throw IllegalArgumentException("Cannot post a null value to this Type. Use normal MutableLiveData instead for that.")
}
fun observe(owner: LifecycleOwner, body: (T) -> Unit) {
observe(owner, Observer<T> {
body(it ?: defaultValue)
})
}
}
Désormais, la valeur dans MutableLiveData ne sera jamais nulle, de sorte que la classe ne sera pas utilisée d'une manière qui n'est pas prévue.
L'utilisation de chaînes est simple en utilisant cette méthode:
val someLiveData = MutableLiveData<String>()
...
someLiveData.value.orEmpty()
Et vous obtiendrez la valeur réelle si elle est définie ou une chaîne vide au lieu de null
.
C'est ma solution.
class NotNullLiveData<T : Any>
constructor(private val default: T) : MediatorLiveData<T>() {
override fun getValue(): T {
return super.getValue() ?: default
}
override fun setValue(value: T) {
super.setValue(value)
}
}
@MainThread
fun <T : Any> mutableLiveDataOfNotNull(initValue: T): NotNullLiveData<T> {
val liveData = NotNullLiveData(initValue)
liveData.value = initValue
return liveData
}
@MainThread
fun <T> mutableLiveDataOf(initValue: T): MutableLiveData<T> {
val liveData = MutableLiveData<T>()
liveData.value = initValue
return liveData
}
fun <T : Any> LiveData<T?>.toNotNull(default: T): NotNullLiveData<T> {
val result = NotNullLiveData(default)
result.addSource(this) {
result.value = it ?: default
}
return result
}