可以在 ViewStub 上使用 BindingAdapter 吗?
Can you use a BindingAdapter on a ViewStub?
我想创建一个 "inflateWhen" BindingAdapter 并将其附加到 viewstub 以使其在布尔值为真时膨胀。但是,BindingAdapter 不断尝试对 viewstub 的根视图进行操作,导致编译失败。有什么方法可以作为绑定适配器来执行此操作,而不必在 activity 中以编程方式执行此操作?
这是我目前的情况:
@BindingAdapter("inflateWhen")
fun inflateWhen(viewstub: ViewStub, inflate: Boolean) {
if (inflate) {
viewstub.inflate()
} else {
viewstub.visibility = View.GONE
}
}
这就是我所拥有的,但是当附加到像
这样的视图存根时
<ViewStub
android:id="@+id/activity_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:inflateWhen="@{viewmodel.userid != 0}" />
编译失败。错误是:
ActivityMyAccountSectionedBindingImpl.java:1087: error: cannot find symbol
if (this.pageFooter.isInflated()) this.pageFooter.getBinding().setVariable(BR.inflateWhen, viewmodelRatingInt0);
看起来它正在尝试将绑定应用于膨胀视图,但这不是我想要的。
08.10.2020 更新:
我在 Medium 上写了一篇文章,其中提供了一个示例,说明如何使用 ViewStub 和 DataBinding 根据屏幕状态动态切换布局:
https://medium.com/@mxdiland/viewstub-databinding-handle-screen-states-easily-2f1c01098b87
旧的接受答案:
我也遇到了为 ViewStub
编写 @BindingAdapter
以使用数据绑定而不是直接引用 ViewStub
并调用 [=16] 来控制布局 inflation 的问题=]
一路上,我做了一些调查,研究了以下内容:
ViewStub
必须具有 android:id
属性以避免构建错误,如 java.lang.IllegalStateException: target.id must not be null;
- 在 XML 中为 ViewStub 声明的任何自定义属性,数据绑定尝试将其设置为布局的变量,而不是存根;
- ... 这就是为什么为
ViewStub
编写的任何绑定适配器永远不会被数据绑定 使用的原因
- 只有一个非常棘手的
@BindingAdapter
有效:androidx.databinding.adapters.ViewStubBindingAdapter
并允许通过 XML 属性 android:onInflate
设置 ViewStub.OnInflateListener
ViewStubBindingAdapter
的第一个参数是 ViewStubProxy
而不是 View
或 ViewStub
!;
- 以类似方式编写的任何不同适配器都不起作用 - 数据绑定尝试将变量设置为未来的布局,而不是使用适配器
- 但是可以覆盖现有的
androidx.databinding.adapters.ViewStubBindingAdapter
并实现一些所需的逻辑。
因为此适配器是唯一一个使用数据绑定与 ViewStub
交互的选项,所以我决定覆盖该适配器并且不用于其预期目的
我们的想法是提供特定的 ViewStub.OnInflateListener
,它将是监听器本身,同时也是一个信号,表明 ViewStub.inflate()
应该被调用:
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
}
}
}
}
和覆盖绑定适配器:
@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()
}
}
和XML部分
...
<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}"
/>
...
所以现在 inflation 只会在状态为 State.Empty
时发生并且数据绑定会将 viewModel 变量设置为膨胀的 @layout/no_data
布局.
不是很优雅但可行的解决方案。
我想创建一个 "inflateWhen" BindingAdapter 并将其附加到 viewstub 以使其在布尔值为真时膨胀。但是,BindingAdapter 不断尝试对 viewstub 的根视图进行操作,导致编译失败。有什么方法可以作为绑定适配器来执行此操作,而不必在 activity 中以编程方式执行此操作?
这是我目前的情况:
@BindingAdapter("inflateWhen")
fun inflateWhen(viewstub: ViewStub, inflate: Boolean) {
if (inflate) {
viewstub.inflate()
} else {
viewstub.visibility = View.GONE
}
}
这就是我所拥有的,但是当附加到像
这样的视图存根时<ViewStub
android:id="@+id/activity_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:inflateWhen="@{viewmodel.userid != 0}" />
编译失败。错误是:
ActivityMyAccountSectionedBindingImpl.java:1087: error: cannot find symbol
if (this.pageFooter.isInflated()) this.pageFooter.getBinding().setVariable(BR.inflateWhen, viewmodelRatingInt0);
看起来它正在尝试将绑定应用于膨胀视图,但这不是我想要的。
08.10.2020 更新:
我在 Medium 上写了一篇文章,其中提供了一个示例,说明如何使用 ViewStub 和 DataBinding 根据屏幕状态动态切换布局:
https://medium.com/@mxdiland/viewstub-databinding-handle-screen-states-easily-2f1c01098b87
旧的接受答案:
我也遇到了为 ViewStub
编写 @BindingAdapter
以使用数据绑定而不是直接引用 ViewStub
并调用 [=16] 来控制布局 inflation 的问题=]
一路上,我做了一些调查,研究了以下内容:
ViewStub
必须具有android:id
属性以避免构建错误,如 java.lang.IllegalStateException: target.id must not be null;- 在 XML 中为 ViewStub 声明的任何自定义属性,数据绑定尝试将其设置为布局的变量,而不是存根;
- ... 这就是为什么为
ViewStub
编写的任何绑定适配器永远不会被数据绑定 使用的原因
- 只有一个非常棘手的
@BindingAdapter
有效:androidx.databinding.adapters.ViewStubBindingAdapter
并允许通过 XML 属性android:onInflate
设置 ViewStubBindingAdapter
的第一个参数是ViewStubProxy
而不是View
或ViewStub
!;- 以类似方式编写的任何不同适配器都不起作用 - 数据绑定尝试将变量设置为未来的布局,而不是使用适配器
- 但是可以覆盖现有的
androidx.databinding.adapters.ViewStubBindingAdapter
并实现一些所需的逻辑。
ViewStub.OnInflateListener
因为此适配器是唯一一个使用数据绑定与 ViewStub
交互的选项,所以我决定覆盖该适配器并且不用于其预期目的
我们的想法是提供特定的 ViewStub.OnInflateListener
,它将是监听器本身,同时也是一个信号,表明 ViewStub.inflate()
应该被调用:
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
}
}
}
}
和覆盖绑定适配器:
@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()
}
}
和XML部分
...
<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}"
/>
...
所以现在 inflation 只会在状态为 State.Empty
时发生并且数据绑定会将 viewModel 变量设置为膨胀的 @layout/no_data
布局.
不是很优雅但可行的解决方案。