可以在ViewStub上使用BindingAdapter吗?

时间:2019-06-26 19:06:18

标签: android data-binding android-databinding

我想创建一个“ inflateWhen” BindingAdapter并将其附加到一个viewtub中,以便在布尔值为true时使其膨胀。但是,BindingAdapter一直试图在viewstub的根视图上进行操作,从而导致其无法编译。有什么方法可以作为绑定适配器来执行此操作,而不必在活动中以编程方式执行此操作吗?

这是我到目前为止的内容:

@BindingAdapter("inflateWhen")
fun inflateWhen(viewstub: ViewStub, inflate: Boolean) {
    if (inflate) {
        viewstub.inflate()
    } else {
        viewstub.visibility = View.GONE
    }
}

这就是我所拥有的,但是当连接到像这样的viewstub时

<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);

看起来它正在尝试将绑定应用于膨胀视图,但这不是我想要的。

1 个答案:

答案 0 :(得分:1)

2020年10月10日更新:

我已经在Medium上写了一篇文章,其中提供了一个示例,该示例演示如何使用ViewStub和DataBinding根据屏幕状态动态切换布局:

https://medium.com/@mxdiland/viewstub-databinding-handle-screen-states-easily-2f1c01098b87

旧的可接受答案:

我还面临为@BindingAdapter编写ViewStub的问题,以使用数据绑定而不是直接引用ViewStub并调用inflate()

来控制布局膨胀

一路走来,我做了一些调查,研究了以下内容:

  • ViewStub必须具有android:id属性,以避免类似 java.lang.IllegalStateException:target.id不能为null的构建错误;
  • 在XML中为ViewStub声明的任何自定义属性,数据绑定尝试将设置为版式的变量,而不是stub;
  • ...这就是为什么为ViewStub编写任何绑定适配器都不会被数据绑定使用的原因
  • 只有一个@BindingAdapter却很棘手,它起作用:androidx.databinding.adapters.ViewStubBindingAdapter并允许通过XML属性ViewStub.OnInflateListener设置android:onInflate
  • ViewStubBindingAdapter的第一个参数是ViewStubProxy而不是ViewViewStub!;
  • 类似编写的任何其他适配器均不起作用-数据绑定尝试将变量设置为将来的布局,而不使用适配器
  • 但是允许覆盖现有的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}"
    />
...

因此,现在仅当状态为State.Empty且数据绑定会将 viewModel 变量设置为膨胀的@layout/no_data布局时,才会发生通货膨胀。

不是很优雅,但是可行的解决方案。