问题
I want to create an "inflateWhen" BindingAdapter and attach it to a viewstub to have it inflate when a boolean value is true. However, the BindingAdapter keeps trying to operate on the root view of the viewstub, causing it to fail to compile. Is there any way to do this as a bindingadapter rather than having to do it programmatically in the activity?
Here's what I have so far:
@BindingAdapter("inflateWhen")
fun inflateWhen(viewstub: ViewStub, inflate: Boolean) {
if (inflate) {
viewstub.inflate()
} else {
viewstub.visibility = View.GONE
}
}
This is what I have, but when attached to a viewstub like
<ViewStub
android:id="@+id/activity_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:inflateWhen="@{viewmodel.userid != 0}" />
it fails to compile. The error is:
ActivityMyAccountSectionedBindingImpl.java:1087: error: cannot find symbol
if (this.pageFooter.isInflated()) this.pageFooter.getBinding().setVariable(BR.inflateWhen, viewmodelRatingInt0);
Looks like it's trying to apply the binding to the inflated view, but that's not what I want here.
回答1:
08.10.2020 Update:
I have written an article on Medium where I provide an example of how to switch between layouts on the fly depending on the screen state using ViewStub and DataBinding:
https://medium.com/@mxdiland/viewstub-databinding-handle-screen-states-easily-2f1c01098b87
Old accepted answer:
I also faced the problem to write @BindingAdapter
for the ViewStub
to control layout inflation using databinding instead of direct referencing to the ViewStub
and calling inflate()
Along the way, I did some investigation and studied the following things:
ViewStub
must haveandroid:id
attribute to avoid build errors like java.lang.IllegalStateException: target.id must not be null;- any custom attribute declared for ViewStub in an XML, databinding tries to set as a variable to the layout which will be inflated instead of the stub;
- ... that's why any binding adapter is written for
ViewStub
will never be used by databinding - there is only one but pretty tricky
@BindingAdapter
which works:androidx.databinding.adapters.ViewStubBindingAdapter
and allows settingViewStub.OnInflateListener
trough XML attributeandroid:onInflate
- the
ViewStubBindingAdapter
's first argument isViewStubProxy
notView
orViewStub
!; - any different adapter written similarly does not work - databinding tries to set variable to the future layout instead of using the adapter
- BUT it is allowed to override existing
androidx.databinding.adapters.ViewStubBindingAdapter
and implement some desired logic.
Because this adapter is one and the only option to interact with ViewStub
using databinding I decided to override the adapter and use not for its intended purpose
The idea is to provide specific ViewStub.OnInflateListener
which will be the listener itself and at the same time will be a signal that ViewStub.inflate()
should be called:
class ViewStubInflationProvoker(
listener: ViewStub.OnInflateListener = ViewStub.OnInflateListener { _, _ -> }
) : ViewStub.OnInflateListener by listener {
companion object {
@JvmStatic
fun provideIf(clause: Boolean): ViewStubInflationProvoker? {
return if (clause) {
ViewStubInflationProvoker()
} else {
null
}
}
}
}
and overriding binding adapter:
@BindingAdapter("android:onInflate")
fun setOnInflateListener(
viewStubProxy: ViewStubProxy,
listener: ViewStub.OnInflateListener?
) {
viewStubProxy.setOnInflateListener(listener)
if (viewStubProxy.isInflated) {
viewStubProxy.root.visibility = View.GONE.takeIf { listener == null } ?: View.VISIBLE
return
}
if (listener is ViewStubInflationProvoker) {
viewStubProxy.viewStub?.inflate()
}
}
and XML part
...
<ViewStub
android:id="@+id/no_data_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/no_data"
android:onInflate="@{ViewStubInflationProvoker.provideIf(viewModel.state == State.Empty.INSTANCE)}"
app:viewModel="@{viewModel.noDataViewModel}"
/>
...
So now the inflation will happen only when the state is State.Empty
and databinding will set viewModel variable to the inflated @layout/no_data
layout.
Not really graceful but working solution.
来源:https://stackoverflow.com/questions/56779550/can-you-use-a-bindingadapter-on-a-viewstub