可以在ViewStub上使用BindingAdapter吗? [英] Can you use a BindingAdapter on a ViewStub?

查看:136
本文介绍了可以在ViewStub上使用BindingAdapter吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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
    }
}

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

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}" /> 

它无法编译.错误是:

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.

推荐答案

2020年10月10日更新:

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

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

可接受的旧答案:

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

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 必须具有 android:id 属性,以避免生成错误,例如 java.lang.IllegalStateException:target.id不能为空
  • 在XML中为ViewStub声明的任何自定义属性,数据绑定会尝试将版式设置为变量,而不是stub会被夸大;
  • ...这就是为什么为 ViewStub 编写任何绑定适配器都不会被数据绑定
  • 使用的原因
  • 只有一个但很棘手的 @BindingAdapter 起作用: androidx.databinding.adapters.ViewStubBindingAdapter 并允​​许设置 ViewStub.OnInflateListener 槽XML属性 android:onInflate
  • ViewStubBindingAdapter 的第一个参数是 ViewStubProxy ,而不是 View ViewStub !!
  • 类似编写的任何其他适配器均不起作用-数据绑定尝试将变量设置为将来的布局,而不使用适配器
  • 但允许覆盖现有的 androidx.databinding.adapters.ViewStubBindingAdapter 并实现一些所需的逻辑.
  • ViewStub must have android: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 setting ViewStub.OnInflateListener trough XML attribute android:onInflate
  • the ViewStubBindingAdapter's first argument is ViewStubProxy not View or ViewStub!;
  • 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.

因为此适配器是使用数据绑定与 ViewStub 进行交互的唯一选择,所以我决定重写该适配器,而不将其用于预定目的

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

这个想法是提供特定的 ViewStub.OnInflateListener ,它将是侦听器本身,同时也是一个信号,该信号应调用 ViewStub.inflate():

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
            }
        }
    }
}

和覆盖绑定适配器:

@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 布局.

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.

这篇关于可以在ViewStub上使用BindingAdapter吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆