无法将自定义适配器绑定到包含标签

时间:2020-08-10 11:54:36

标签: android kotlin android-databinding

我正在尝试使用Kotlin的Sealed类来处理我的应用程序中的状态。我在不同的布局文件中分离了不同的状态,以供重复使用。但是问题是,即使我能够成功地在一个片段中使用它,它也无法在另一个片段中工作。对于此错误,日志非常令人困惑。也许有一个微妙的错误,我无法在我的代码中找到。

这是我的密封类,用于状态处理:

sealed class LoadStates {
    object Loading : LoadStates()
    class Error(val msg: String = "Something went wrong") : LoadStates()
    class Empty(val msg: String = "No item") : LoadStates()
    object NotEmpty : LoadStates()

    companion object {
        fun <T> decideEmptyOrNot(list: List<T>?,msg: String): LoadStates {
            return if (list.isNullOrEmpty()) { Empty(msg) } else { NotEmpty }
        }
    }
}

我正在使用自定义绑定适配器:

@BindingAdapter("bindLoadingState")
fun uiStateWhileLoading(view: View, state: LoadStates) {
    view.visibility = if (state is LoadStates.Loading) { View.VISIBLE } else { View.GONE }
}

@BindingAdapter("bindErrorState")
fun uiStateWhenError(view: LinearLayout, state: LoadStates) {
    view.visibility = if(state is LoadStates.Error) {
        val msg = view.getChildAt(1) as TextView
        msg.text = state.msg
        View.VISIBLE
    } else { View.GONE }
}

@BindingAdapter("bindEmptyState")
fun uiStateWhenEmpty(view: LinearLayout, state: LoadStates) {
    if(state is LoadStates.Empty) {
        val msg = view.getChildAt(1) as TextView
        msg.text = state.msg
        view.visibility = View.VISIBLE
    } else {
        view.visibility = View.GONE
    }
}

@BindingAdapter("bindNonEmptyState")
fun uiStateWhenNotEmpty(view: View, state: LoadStates) {
    view.visibility = if (state is LoadStates.NotEmpty) { View.VISIBLE } else { View.INVISIBLE }
}

@BindingAdapter("bindDisableState")
fun bindButtonDisableState(view: View, state: LoadStates) {
    view.isEnabled = state !is LoadStates.Loading
}

我的布局文件的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"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="presenter"
            type="bd.edu.daffodilvarsity.classmanager.features.admin.bookedrooms.rangeddate.BookedRoomsRangedDate" />
        <variable
            name="viewModel"
            type="bd.edu.daffodilvarsity.classmanager.features.admin.bookedrooms.common.BookedRoomsAdminViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".features.admin.bookedrooms.rangeddate.BookedRoomsRangedDate">

        ...
        ......
        ............

        <include
            layout="@layout/state_empty"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:state="@{viewModel.uiStates}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/top_view"/>

        <include
            layout="@layout/state_loading"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:bindLoadingState="@{viewModel.uiStates}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/top_view" />

        <include
            layout="@layout/state_error"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:state="@{viewModel.uiStates}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/top_view"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:bindNonEmptyState="@{viewModel.uiStates}"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@id/top_view"
            app:layout_constraintBottom_toBottomOf="parent"/>

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="18dp"
            android:onClick="@{() -> presenter.fetchBookedRooms()}"
            android:src="@drawable/ic_search"
            app:bindDisableState="@{viewModel.uiStates}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

我的3种状态的XML文件:

state_loading:https://pastebin.com/AhwvV69P

state_error:https://pastebin.com/TnfynHNi

state_empty:https://pastebin.com/xLeQQ1GX

我非常确定我的ui状态livedata不为null。这是崩溃日志:

2020-08-10 17:34:14.837 15167-15167/bd.edu.daffodilvarsity.classmanager E/AndroidRuntime: FATAL EXCEPTION: main
    Process: bd.edu.daffodilvarsity.classmanager, PID: 15167
    java.lang.RuntimeException: Failed to call observer method
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:226)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185)
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37)
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361)
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300)
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339)
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145)
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131)
        at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:51)
        at androidx.fragment.app.Fragment.performStart(Fragment.java:2737)
        at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:365)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1194)
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
        at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:915)
     Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter state
        at bd.edu.daffodilvarsity.classmanager.common.adapter.CustomBindingAdaptersKt.uiStateWhileLoading(Unknown Source:7)
        at bd.edu.daffodilvarsity.classmanager.databinding.FragmentBookedRoomsRangedDateBindingImpl.executeBindings(FragmentBookedRoomsRangedDateBindingImpl.java:206)
        at androidx.databinding.ViewDataBinding.executeBindingsInternal(ViewDataBinding.java:473)
        at androidx.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:445)
        at androidx.databinding.ViewDataBinding$OnStartListener.onStart(ViewDataBinding.java:1687)
        at java.lang.reflect.Method.invoke(Native Method)
        at androidx.lifecycle.ClassesInfoCache$MethodReference.invokeCallback(ClassesInfoCache.java:216)
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeMethodsForEvent(ClassesInfoCache.java:194) 
        at androidx.lifecycle.ClassesInfoCache$CallbackInfo.invokeCallbacks(ClassesInfoCache.java:185) 
        at androidx.lifecycle.ReflectiveGenericLifecycleObserver.onStateChanged(ReflectiveGenericLifecycleObserver.java:37) 
        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:361) 
        at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:300) 
        at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:339) 
        at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:145) 
        at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:131) 
        at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:51) 
        at androidx.fragment.app.Fragment.performStart(Fragment.java:2737) 
        at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:365) 
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1194) 
        at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224) 
        at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997) 
        at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953) 
        at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849) 
        at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413) 
        at android.os.Handler.handleCallback(Handler.java:883) 
        at android.os.Handler.dispatchMessage(Handler.java:100) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7356) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:915) 

1 个答案:

答案 0 :(得分:1)

错误是您没有在以下布局绑定中传递LoadState的值

@BindingAdapter("bindLoadingState")
fun uiStateWhileLoading(view: View, state: LoadStates) {
    view.visibility = if (state is LoadStates.Loading) { View.VISIBLE } else { View.GONE }
}

这里状态为空,因为您没有在 state_loading.xml 布局本身中绑定变量。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <!--add missing state binding variable here-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">
        <ProgressBar
            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:indeterminate="true" />
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginBottom="8dp"
            android:text="@string/loading_data"
            android:textSize="18sp"
            android:textColor="?android:attr/textColorPrimary"/>
    </LinearLayout>
</layout>

现在我想你知道该怎么做。只需添加绑定变量,就像在上述文件中的其他布局文件中添加的一样。

<data>
      <variable
      name="state"  type="bd.edu.daffodilvarsity.classmanager.common.models.LoadStates" />
</data>

修改

尝试使适配器函数参数为可空值,该参数在启动时可能为空。

@BindingAdapter("bindLoadingState")
fun uiStateWhileLoading(view: View?, state: LoadStates?) {
    if(view!=null && state!=null)
    view.visibility = if (state is LoadStates.Loading) { View.VISIBLE } else { View.GONE }
}

@BindingAdapter("bindErrorState")
fun uiStateWhenError(view: LinearLayout?, state: LoadStates?) {
    if(view!=null && state!=null)
    view.visibility = if(state is LoadStates.Error) {
        val msg = view.getChildAt(1) as TextView
        msg.text = state.msg
        View.VISIBLE
    } else { View.GONE }
}

@BindingAdapter("bindEmptyState")
fun uiStateWhenEmpty(view: LinearLayout?, state: LoadStates?) {
    if(view!=null && state!=null)
    if(state is LoadStates.Empty) {
        val msg = view.getChildAt(1) as TextView
        msg.text = state.msg
        view.visibility = View.VISIBLE
    } else {
        view.visibility = View.GONE
    }
}

@BindingAdapter("bindNonEmptyState")
fun uiStateWhenNotEmpty(view: View?, state: LoadStates?) {
    if(view!=null && state!=null)
    view.visibility = if (state is LoadStates.NotEmpty) { View.VISIBLE } else { View.INVISIBLE }
}

@BindingAdapter("bindDisableState")
fun bindButtonDisableState(view: View?, state: LoadStates?) {
    if(view!=null && state!=null)
    view.isEnabled = state !is LoadStates.Loading
}
相关问题