【问题标题】:BindingAdapter not recognized as applicable to property with genericsBindingAdapter 不适用于具有泛型的属性
【发布时间】:2017-12-07 12:26:46
【问题描述】:

ObservableList<Subclass>@BindingAdapter 声明与ObservableList<Baseclass> 匹配的行为方式很奇怪。


我正在尝试在 MVVM 上下文中使用 android 数据绑定库,但偶然发现了一种我不理解且到目前为止还没有找到任何资源的行为。

设置:

Min SDK: 19
Target SDK: 26
Build Tools Version: 27.0.1

我想使用自定义绑定适配器将android.databinding.ObservableList<Model> 绑定到RecyclerView,该适配器接受列表,将其包装成通用RecyclerView.Adapter 并将其分配给RecyclerView

我的绑定适配器是这样设置的:

@BindingAdapter({"items"})
public static void bindAdapterWithDefaultBinder(@NonNull RecyclerView recyclerView,
                                                @Nullable ObservableList<Model> items){
... implementation ...
}

我的视图模型具有以下属性:

public final ObservableList<Repo> repos = new ObservableArrayList<>();

Repo 在这种情况下实现了接口Model

并且布局文件包含

<android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:items="@{vm.repos}"
            />

编译时收到以下错误:

Error:Gradle: Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.RuntimeException: Found data binding errors.
  ****/ data binding error ****msg:Cannot find the setter for attribute 'app:items' with parameter type android.databinding.ObservableList<_my-package-name_.Repo> on android.support.v7.widget.RecyclerView.

所以我认为这与类型 Repo 在绑定转换器声明中未被识别为 Model 的有效替代品有关。奇怪,但确实如此 - 如果我将声明更改为使用 ObservableList&lt;Repo&gt; items,则代码可以正常编译并且可以正常工作。但这对我来说不是一个可行的解决方案,因为这将迫使我为我打算使用的每个模型或视图模型类型编写一个绑定适配器。所以经过一些实验,我找到了一个不同的解决方案:

    <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:items="@{(Object)vm.repos}"
            />

现在,此解决方案使用 ObservableList&lt;Model&gt; items 适配器声明进行编译,并在运行时正确传播所有更改事件。所以基本上我将ObservableList&lt;Repo&gt; 转换为Object,它被解释为匹配适配器定义。什么??????

请记住,所有这些都发生在编译时,代码生成器知道确切的类型,因此不会发生类型擦除。

所以我的问题是:

  • 为什么第一次实现会失败?
  • 为什么强制转换为 Object 可以解决问题?
  • 因为这对我来说看起来像一个丑陋的 hack - 实现这种行为的正确方法是什么?

附:

关于这个问题的更多背景信息,我基本上是通过这个示例代码来工作的,我发现它是一个很好的灵感来源:https://github.com/manas-chaudhari/android-mvvm/blob/master/Documentation/GettingStarted.md

但请注意作者如何厚颜无耻地将他的所有模型对象“强制转换”为基类类型以避免这个确切的问题here

【问题讨论】:

    标签: java android mvvm android-databinding


    【解决方案1】:

    我认为你应该像这样去掉大括号:

    @BindingAdapter("items")   
    public static void bindAdapterWithDefaultBinder(@NonNull RecyclerView recyclerView, @Nullable ObservableList<Model> items) {
    ... implementation ...
    }
    

    【讨论】:

    • 感谢您的建议。帖子中的代码是原始代码的简化版本,其中实际注释为@BindingAdapter(value = {"items", "viewProvider", "itemActionResponder"}, requireAll = false),因此无法在此处删除大括号。这也不会改变行为,因为实例化的注释具有 String[] 的值,并且省略大括号只是在此处添加单个元素的简写。
    • 不,还没有。正如问题中所提出的那样,有一个丑陋的解决方法。但我仍然不明白这里发生了什么以及为什么。免责声明:我没有使用所涉及的较新版本的 API 对此进行测试。
    【解决方案2】:

    这是因为List 在 Java 中不是协变的。这意味着List&lt;Subclass&gt; 不是List&lt;Baseclass&gt;,因此绑定引擎没有理由考虑您的适配器。你需要:

    @BindingAdapter({"items"})
    public static <T extends Model> void bindAdapterWithDefaultBinder(@NonNull RecyclerView recyclerView,
                                                @Nullable ObservableList<T> items){
    

    还有泛型。

    【讨论】:

      猜你喜欢
      • 2019-02-16
      • 2023-02-11
      • 1970-01-01
      • 2012-09-23
      • 2019-05-29
      • 1970-01-01
      • 1970-01-01
      • 2020-06-13
      • 1970-01-01
      相关资源
      最近更新 更多