2

2019 年 11 月更新 - 它现在在最新版本上按预期工作。

原帖:

我正在通过公开公开的函数 setRefreshing(应用程序:XML 中的刷新)将 MutableLiveData 绑定到我的 SwipeRefreshLayout 并且当时一切正常......但是让我们介绍一下我的应用程序架构。

当我根据刷新状态更改其值时,我有带有 MutableLiveData 的抽象 ViewModel。

然后我有两个从这个抽象继承的 ViewModel(让我将它们命名为 FirstViewModel 和 SecondViewModel),命名为 BaseRefreshViewModel。首先,我有两个几乎相同的 XML 文件,仅在第一个 XML 中导入 FirstViewModel 和第二个 - 对应的 SecondViewModel 时与“数据”节点不同。

我太可怕了,所以我将它合并到一个 XML 中并导入这个 BaseRefreshViewModel (list_layout.xml):

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable name="viewModel"
                type="my.package.BaseRefreshViewModel" />
    </data>

    <androidx.coordinatorlayout.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/coordinator_layout">

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:refreshing="@{viewModel.isRefreshing}"
                android:id="@+id/swipe_layout">

            <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/station_list"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:adapter="@{viewModel.stations}"/>

        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

然后编译器开始发疯 - 它说:

Cannot find a setter for <androidx.swiperefreshlayout.widget.SwipeRefreshLayout app:refreshing> that accepts parameter type 'androidx.lifecycle.MutableLiveData'

If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.

好的,所以我编写了自己的 BindingAdapter(当然在 SwipeRefreshLayout 中更改为 app:refresh):

@BindingAdapter("refresh")
fun setRefreshing(view: SwipeRefreshLayout, refreshing: Boolean) {
    view.isRefreshing = refreshing
}

仍然是同样的问题,然后我将 BindingAdapter 更改为:

@BindingAdapter("refresh")
fun setRefreshing(view: SwipeRefreshLayout, refreshing: MutableLiveData<Boolean>) {
    refreshing.value?.let { view.isRefreshing }
}

它开始编译,但在运行我的应用程序后出现错误:

Caused by: java.lang.ClassCastException: java.lang.Boolean cannot be cast to androidx.lifecycle.MutableLiveData

没有狗屎夏洛克...有趣的是,当我将 XML 文件中的导入从 BaseRefreshViewModel 更改为 FirstViewModel/SecondViewModel 时,即使没有我的 BindingAdapter,它也开始编译得很好(我当然不能这样,因为我有一个不同的列表我绑定到我的适配器的 ViewModels 中的对象)。

这是我在片段中的 ViewModel 初始化:

lateinit var stationViewModel: FirstViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        stationViewModel = ViewModelProviders.of(requireActivity()).get(FirstViewModel::class.java)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.list_layout, container, false)
        binding.viewModel = stationViewModel
        binding.lifecycleOwner = this
        return binding.root
    }

和 ViewModel 本身:

abstract class BaseRefreshViewModel(application: Application) : AndroidViewModel(application) {

    val isRefreshing = MutableLiveData<Boolean>().apply { value = false }


    val receiver = object : StatusReceiver.Receiver {
        override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
            when (resultCode) {
                StatusReceiver.STATUS_RUNNING -> isRefreshing.value = true
                StatusReceiver.STATUS_IDLE -> isRefreshing.value = false
                StatusReceiver.STATUS_NO_CONNECTION -> isRefreshing.value = false
                StatusReceiver.STATUS_ERROR -> isRefreshing.value = false
            }
        }
    }

    abstract fun refresh()

}

如何在不返回创建两个导入了不同 ViewModel 的 XML 文件的情况下超越这一点?

我使用 Android Studio 3.5 Beta 5 只是为了利用 DataBinding 改进的错误消息。

更新:

当我将 MutableLiveData 更改为 ObservableBoolean() 时,它编译并运行良好......但我不想坚持这一点,我想使用 LiveData 及其生命周期优势。它只是显示了我认为现在数据绑定编译器是如何被窃听的。

概括:

WORKING(两个不同的xml,几乎一样)

  • BaseRefreshViewModel (isRefreshing: MutableLiveData)
    • 第一视图模型
      • first_list_layout.xml(导入 FirstViewModel)
    • 第二视图模型
      • second_list_layout.xml(导入 SecondViewModel)

工作(一个 xml 文件,但不是 LiveData)

  • BaseRefreshViewModel (isRefreshing: ObservableBoolean)
    • 第一视图模型
      • list_layout.xml(导入 BaseRefreshViewModel)
    • 第二视图模型
      • list_layout.xml(导入 BaseRefreshViewModel)

不工作(一个带有 LiveData 的 xml 文件)

  • BaseRefreshViewModel (isRefreshing: MutableLiveData)
    • 第一视图模型
      • list_layout.xml(导入 BaseRefreshViewModel)
    • 第二视图模型
      • list_layout.xml(导入 BaseRefreshViewModel)
4

5 回答 5

3

kotlin-kapt在 kotlin 项目上应用插件后为我工作

apply plugin: 'kotlin-kapt'

之后,当绑定到字段时,将LiveData<T>展开。T

作为记录,我还包括这些库

    // Lifecycle
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc01'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc01'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc01'
于 2019-10-29T11:11:02.830 回答
1

今天也遇到了这个错误。一种可能但有点难看的解决方法是让您的 BindAdapter 接受 Object 然后将其转换为您需要的任何内容。

改变 @BindingAdapter("refresh") fun setRefreshing(view: SwipeRefreshLayout, refreshing: Boolean)

@BindingAdapter("refresh") fun setRefreshing(view: SwipeRefreshLayout, refreshing: Object)

然后将refreshing其投射到Boolean

于 2019-08-23T07:02:14.963 回答
1

另一种选择是使用数据绑定 3.4.1,它按预期工作。

    dataBinding {
        enabled = true
        version = "3.4.1"
    }
于 2019-10-23T11:53:14.730 回答
0

在您的 XML 中:

 <android.support.v4.widget.SwipeRefreshLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 app:refreshing="@{viewModel.isLoading}"
 app:onRefreshListener="@{() -> viewModel.onRefresh()}">

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/station_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:adapter="@{viewModel.stations}"/>

    </android.support.v4.widget.SwipeRefreshLayout>

在您的 ViewModel 中:

    public MutableLiveData <Boolean >isLoading = new MutableLiveData ();

/* Needs to be public for Databinding */
public void onRefresh() {
    isLoading.setValue(true);
    // your logic
}

public void onError(Exception oops){
    isLoading.setValue(false);
    Log.e("Stack", oops);
}
于 2019-07-17T07:14:00.010 回答
0

嗨希望你做得好。

问题出在app:refreshing="@{viewModel.isRefreshing}".

属性app:refershing只接受布尔值。而你试图给它一个LiveData值。这导致

Caused by: java.lang.ClassCastException: java.lang.Boolean cannot be cast to androidx.lifecycle.MutableLiveData

所以你可以做的是:

  1. 在Data标签中创建一个布尔类型的变量

    <variable
        name="refreshing"
        type="Boolean" />
    
  2. 观察你的 MutableLiveData

  3. 在观察者 中将布尔变量设置为DataBinding变量。yourBinding.setRefreshing(yourObserverBooleanVariable);

注:我是按照java语法写的

于 2019-07-16T07:52:18.423 回答