NOVEMBER 2019 UPDATE - it is working as intended now on the latest version.
ORIGINAL POST:
I'm binding MutableLiveData to my SwipeRefreshLayout via publicly exposed function setRefreshing (app:refreshing in XML) and everything works fine by the time... But let in introduce my app architecture.
I have abstract ViewModel with MutableLiveData when I change its value according to refresh the status.
Then I have two ViewModels (let me name them FirstViewModel and SecondViewModel) inherited from this abstract, name it BaseRefreshViewModel. First I had two practically identical XML files, differing only with "data" node when in first XML I import FirstViewModel and in second - corresponding SecondViewModel.
I was horrible, so I merged this into one XML and import this BaseRefreshViewModel (list_layout.xml):
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="viewModel"
type="my.package.BaseRefreshViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/coordinator_layout">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:refreshing="@{viewModel.isRefreshing}"
android:id="@+id/swipe_layout">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/station_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:adapter="@{viewModel.stations}"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
And then compiler start going crazy - it says:
Cannot find a setter for <androidx.swiperefreshlayout.widget.SwipeRefreshLayout app:refreshing> that accepts parameter type 'androidx.lifecycle.MutableLiveData'
If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.
Ok, so I wrote my own BindingAdapter (changing of course to app:refresh in SwipeRefreshLayout):
@BindingAdapter("refresh")
fun setRefreshing(view: SwipeRefreshLayout, refreshing: Boolean) {
view.isRefreshing = refreshing
}
Still the same issue, then I changed BindingAdapter to:
@BindingAdapter("refresh")
fun setRefreshing(view: SwipeRefreshLayout, refreshing: MutableLiveData<Boolean>) {
refreshing.value?.let { view.isRefreshing }
}
And it starts compiling, but after run my app crash with error:
Caused by: java.lang.ClassCastException: java.lang.Boolean cannot be cast to androidx.lifecycle.MutableLiveData
No shit Sherlock... What is funny that when I change import in my XML file from BaseRefreshViewModel to FirstViewModel/SecondViewModel it starts compiling just fine even without my BindingAdapter (I can't leave it like this of course because I have a different list of object in ViewModels which I'm binding to my adapter).
Here is my ViewModel initialization in fragment:
lateinit var stationViewModel: FirstViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
stationViewModel = ViewModelProviders.of(requireActivity()).get(FirstViewModel::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.list_layout, container, false)
binding.viewModel = stationViewModel
binding.lifecycleOwner = this
return binding.root
}
And ViewModel itself:
abstract class BaseRefreshViewModel(application: Application) : AndroidViewModel(application) {
val isRefreshing = MutableLiveData<Boolean>().apply { value = false }
val receiver = object : StatusReceiver.Receiver {
override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
when (resultCode) {
StatusReceiver.STATUS_RUNNING -> isRefreshing.value = true
StatusReceiver.STATUS_IDLE -> isRefreshing.value = false
StatusReceiver.STATUS_NO_CONNECTION -> isRefreshing.value = false
StatusReceiver.STATUS_ERROR -> isRefreshing.value = false
}
}
}
abstract fun refresh()
}
How can I overpass this without going back to creating two XML files with different ViewModel imported?
I'm using Android Studio 3.5 Beta 5 just to take advantage with improved error messages with DataBinding.
UPDATE:
When I change MutableLiveData to ObservableBoolean() it compile and run fine... But I don't wanna stick with this, I want to use LiveData with its Lifecycle advantages. It's just shows how Databinding compiler is bugged right now I think.
SUMMARY:
WORKING (two different xml, practically the same)
WORKING (one xml file, but not LiveData)
NOT WORKING (one xml file with LiveData)
Worked for me after applying the plugin kotlin-kapt
on a kotlin project
apply plugin: 'kotlin-kapt'
after that, the LiveData<T>
is unwrapped into T
when bound to fields.
For the record, I also include these libraries
// Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc01'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc01'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc01'