【发布时间】: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<Repo> items,则代码可以正常编译并且可以正常工作。但这对我来说不是一个可行的解决方案,因为这将迫使我为我打算使用的每个模型或视图模型类型编写一个绑定适配器。所以经过一些实验,我找到了一个不同的解决方案:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:items="@{(Object)vm.repos}"
/>
现在,此解决方案使用 ObservableList<Model> items 适配器声明进行编译,并在运行时正确传播所有更改事件。所以基本上我将ObservableList<Repo> 转换为Object,它被解释为匹配适配器定义。什么??????
请记住,所有这些都发生在编译时,代码生成器知道确切的类型,因此不会发生类型擦除。
所以我的问题是:
- 为什么第一次实现会失败?
- 为什么强制转换为
Object可以解决问题? - 因为这对我来说看起来像一个丑陋的 hack - 实现这种行为的正确方法是什么?
附:
关于这个问题的更多背景信息,我基本上是通过这个示例代码来工作的,我发现它是一个很好的灵感来源:https://github.com/manas-chaudhari/android-mvvm/blob/master/Documentation/GettingStarted.md
但请注意作者如何厚颜无耻地将他的所有模型对象“强制转换”为基类类型以避免这个确切的问题here。
【问题讨论】:
标签: java android mvvm android-databinding