我正在尝试使用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)
答案 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
}