if I have a class
data class item(val address: String = ""
)
its declared in my viewmodel
var varLive: MutableLiveData = MutableLiveData()
and later on I post it from my viewmodel
varLive.postValue(scootersList[marker])
in my xml I have
<TextView
...
android:text="#{vModel.varLive.address}"
/>
And I can't access item.address and get a databinding error.
I can check if the varLive is null and tht is it
Do I really have to declare each of the livedata class fields as a live data? If I have a class holding 100 members?
for some stupid reason you have to specify a getter method in your viewmodel, so databinding can pick it up. like so:
fun getvarLive() = varLive
Kotlin actially does that for you. But databinding won't bind Kotlin getters. Seriosly annoying
Related
I am new in Kotlin and confused in underscore in a variable as _view.
Please help me.
class MainActivityPresenter(_view: View): Presenter {
private var view: View = _view
Maybe you can read about Names for backing properties and you'll understand it better.
If a class has two properties which are conceptually the same but one is part of a public API and another is an implementation detail, use an underscore as the prefix for the name of the private property
This is common in viewModel where you have your private val of a MutableLiveData and then you have the same name variable without the underscore that is the one is accessible from outside but not mutable.
Example :
private val _user: MutableLiveData<User>
val user: LiveData<User>
get() = _user
In your case would be to don't confuse the View that is in your Presenter and the View that is by constructor.
I have two variables inside my layout file :
<data>
<variable name="createExpenseViewModel" type="com.lionosur.dailyexpenses.viewModels.MainViewModel"/>
<variable name="createExpenseConverter" type="com.lionosur.dailyexpenses.converters.createExpenseActivityConverter.Companion"/>
</data>
My view model has an method to return the live data :
fun getAllExpenseItems(): LiveData<List<Expense>> {
return expenseRepository.getAllExpenseItems()
}
I need to observe this data and populate an spinner,
class createExpenseActivityConverter {
// contains all the static methods to convert the data for the ui
companion object {
fun getExpenseCategoryListFromSource(list:List<Source>):ArrayList<String> {
val categoryItems = ArrayList<String>()
categoryItems.addAll(list.map { it.sourceName })
return categoryItems
}
}
}
to populate a spinner I need to supply an array list of string
<Spinner
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/expense_category"
android:entries="#{()-> createExpenseViewModel.getAllSourceItems(1) }"
app:layout_constraintStart_toStartOf="#+id/textView"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="#+id/textView" app:layout_constraintWidth_percent="0.7"
/>
in android:entries I need to convert the observed data to array list of string, how do I pass the #{()-> createExpenseViewModel.getAllSourceItems(1) } result in to another static method createExpenseViewConverter.getExpenseCategoryListFromSource(sourceList) which would return a array list of string.
in my activity i have setup binding like this
binding = DataBindingUtil.setContentView(this, R.layout.activity_create_expense)
val mainViewModel = DaggerExpenseComponent.builder()
.setContext(this)
.build()
.getExpenseViewModel()
binding.setLifecycleOwner(this)
binding.createExpenseViewModel = mainViewModel
You'll need to use below syntax for that :
android:entries="#{createExpenseConverter.getExpenseCategoryListFromSource(createExpenseViewModel.getAllSourceItems(1))}"
Here, what we've done is accessed your input from MainViewModel object createExpenseViewModel using getAllSourceItems() method;
And then passing it to another class createExpenseActivityConverter object createExpenseConverter using method getExpenseCategoryListFromSource() which returns you ArrayList<String> that your spinner requires.
Edit:
When you use LiveData in DataBinding, Data-binding Compiler takes care of refreshing data just like ObservableFields. All you need to do is provide your LifeCycleOwner to your databinding object.
For Example:
If your activity has ViewDataBinding let's say mActivityBinding using which you provide your ViewModel to set LiveData in xml binding, then after setting your ViewModel consider setting LifecycleOwner like below code :
//Some Activity having data-binding
... onCreate() method of activity
mActivityBinding.setViewModel(myViewModel);
mAcivityBinding.setLifecycleOwner(this); // Providing this line will help you observe LiveData changes from ViewModel in data-binding.
...
Refer here
I have a custom class Card. I use it to create an array using arrayOfNulls() like this:
var cards: Array<Array<Card?>?> = Array(n) { arrayOfNulls<Card?>(n) }
where n is an Int(Kotlin).
Now I need to move this variable in my ViewModel class and wrap it around a LiveData class (preferably MutableLiveData).
How should I declare & initialize the cards variable in my AndroidViewModel class?
Create a MutableLiveData-variable, that wraps your cards and add it as class variable inside your ViewModel:
val mutableLiveData = MutableLiveData<Array<Array<Card?>?>>()
You can now access and subscribe to this variable from your Fragment/Activity/...
To change the value of the mutableLiveData-Variable just change the value:
mutableLiveData .value = cards
and all subscribers of this LiveData-Variable will get the new value.
I'm trying out databinding for a view that's supposed to display data exposed through a LiveData property in a viewmodel, but I've found no way to bind the object inside the LiveData to the view. From the XML I only have access to the value property of the LiveData instance, but not the object inside it. Am I missing something or isn't that possible?
My ViewModel:
class TaskViewModel #Inject
internal constructor(private val taskInteractor: taskInteractor)
: ViewModel(), TaskContract.ViewModel {
override val selected = MutableLiveData<Task>()
val task: LiveData<Task> = Transformations.switchMap(
selected
) { item ->
taskInteractor
.getTaskLiveData(item.task.UID)
}
... left out for breivety ...
}
I'm trying to bind the values of the task object inside my view, but when trying to set the values of my task inside my view I can only do android:text="#={viewmodel.task.value}". I have no access to the fields of my task object. What's the trick to extract the values of your object inside a LiveData object?
My task class:
#Entity(tableName = "tasks")
data class Task(val id: String,
val title: String,
val description: String?,
created: Date,
updated: Date,
assigned: String?)
For LiveData to work with Android Data Binding, you have to set the LifecycleOwner for the binding
binding.setLifecycleOwner(this)
and use the LiveData as if it was an ObservableField
android:text="#{viewmodel.task}"
For this to work, Task needs to implement CharSequence. Using viewmodel.task.toString() might work as well. To implement a two-way-binding, you'd have to use MutableLiveData instead.
why are you using two way binding for TextView
android:text="#={viewmodel.task.value}"
instead use like this android:text="#{viewmodel.task.title}"
I have a data class in Kotlin that inherits from a Java class, which defines a constructor with 1 argument,
public BaseClass(String userSessionId) {
this.userSessionId = userSessionId;
}
My Kotlin class is defined as this
class DerivedClass(
userSessionId: String,
var other: Other? = null
) : BaseClass(userSessionId) {
I can't define it as a data class because of userSessionId, which Kotlin requires to be a val or var in data classes. However, if I do so, then Retrofit throws an exception because there are 2 members named userSessionId. Is there a way to have a data class inherit from a Java class with a constructor taking arguments? Note that I cannot change the base class.
A possible solution is to define a dummy val to avoid the name clash, but this is less than ideal
data class DerivedClass(
val dummy: String,
var other: Other? = null
) : BaseClass(dummy) {
You can use the transient keyword in Java to ignore a field during serialization, this can be done in Kotlin by using the #Transient annotation on the property instead.