#android #kotlin #android-databindin&
#Android #kotlin #android-привязка к данным
Вопрос:
Я пытаюсь использовать закрытый класс Kotlin для обработки состояний в моем приложении. Я разделил разные состояния в разных файлах макета для их повторного использования. Но проблема в том, что, хотя я могу успешно использовать его во фрагменте, он не работает в другом фрагменте. Эта ошибка приводит к путанице в журнале. Возможно, есть небольшая ошибка, которую я не могу найти в своем коде.
Это мой закрытый класс, который используется для обработки состояния :
sealed class LoadStates {
object Loadin& : LoadStates()
class Error(val ms&: Strin& = "Somethin& went wron&") : LoadStates()
class Empty(val ms&: Strin& = "No item") : LoadStates()
object NotEmpty : LoadStates()
companion object {
fun <T&&t; decideEmptyOrNot(list: List<T&&t;?,ms&: Strin&): LoadStates {
return if (list.isNullOrEmpty()) { Empty(ms&) } else { NotEmpty }
}
}
}
Существует пользовательский адаптер привязки, который я использую :
@Bindin&Adapter("bindLoadin&State")
fun uiStateWhileLoadin&(view: View, state: LoadStates) {
view.visibility = if (state is LoadStates.Loadin&) { View.VISIBLE } else { View.GONE }
}
@Bindin&Adapter("bindErrorState")
fun uiStateWhenError(view: LinearLayout, state: LoadStates) {
view.visibility = if(state is LoadStates.Error) {
val ms& = view.&etChildAt(1) as TextView
ms&.text = state.ms&
View.VISIBLE
} else { View.GONE }
}
@Bindin&Adapter("bindEmptyState")
fun uiStateWhenEmpty(view: LinearLayout, state: LoadStates) {
if(state is LoadStates.Empty) {
val ms& = view.&etChildAt(1) as TextView
ms&.text = state.ms&
view.visibility = View.VISIBLE
} else {
view.visibility = View.GONE
}
}
@Bindin&Adapter("bindNonEmptyState")
fun uiStateWhenNotEmpty(view: View, state: LoadStates) {
view.visibility = if (state is LoadStates.NotEmpty) { View.VISIBLE } else { View.INVISIBLE }
}
@Bindin&Adapter("bindDisableState")
fun bindButtonDisableState(view: View, state: LoadStates) {
view.isEnabled = state !is LoadStates.Loadin&
}
XML-код моего файла макета :
<?xml version="1.0" encodin&="utf-8"?&&t;
<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"&&t;
<data&&t;
<variable
name="presenter"
type="bd.edu.daffodilvarsity.classmana&er.features.admin.bookedrooms.ran&eddate.BookedRoomsRan&edDate" /&&t;
<variable
name="viewModel"
type="bd.edu.daffodilvarsity.classmana&er.features.admin.bookedrooms.common.BookedRoomsAdminViewModel" /&&t;
</data&&t;
<androidx.constraintlayout.wid&et.ConstraintLayout
android:layout_width="match_parent"
android:layout_hei&ht="match_parent"
tools:context=".features.admin.bookedrooms.ran&eddate.BookedRoomsRan&edDate"&&t;
...
......
............
<include
layout="@layout/state_empty"
android:layout_width="0dp"
android:layout_hei&ht="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"/&&t;
<include
layout="@layout/state_loadin&"
android:layout_width="0dp"
android:layout_hei&ht="0dp"
app:bindLoadin&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" /&&t;
<include
layout="@layout/state_error"
android:layout_width="0dp"
android:layout_hei&ht="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"/&&t;
<androidx.recyclerview.wid&et.RecyclerView
android:id="@ id/recycler_view"
android:layout_width="0dp"
android:layout_hei&ht="0dp"
app:bindNonEmptyState="@{viewModel.uiStates}"
app:layoutMana&er="androidx.recyclerview.wid&et.LinearLayoutMana&er"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/top_view"
app:layout_constraintBottom_toBottomOf="parent"/&&t;
<com.&oo&le.android.material.floatin&actionbutton.Floatin&ActionButton
android:layout_width="wrap_content"
android:layout_hei&ht="wrap_content"
android:layout_mar&in="18dp"
android:onClick="@{() -&&t; presenter.fetchBookedRooms()}"
android:src="@drawable/ic_search"
app:bindDisableState="@{viewModel.uiStates}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/&&t;
</androidx.constraintlayout.wid&et.ConstraintLayout&&t;
</layout&&t;
XML-файлы моих 3-х состояний :
state_loadin& : https://pastebin.com/AhwvV69P
ошибка состояния : https://pastebin.com/TnfynHNi
state_empty : https://pastebin.com/xLeQQ1GX
Я почти уверен, что состояние моего пользовательского интерфейса livedata не равно null. Вот журнал сбоев :
2020-08-10 17:34:14.837 15167-15167/bd.edu.daffodilvarsity.classmana&er E/AndroidRuntime: FATAL EXCEPTION: main
Process: bd.edu.daffodilvarsity.classmana&er, PID: 15167
java.lan&.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.onStateChan&ed(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRe&istry$ObserverWithState.dispatchEvent(LifecycleRe&istry.java:361)
at androidx.lifecycle.LifecycleRe&istry.forwardPass(LifecycleRe&istry.java:300)
at androidx.lifecycle.LifecycleRe&istry.sync(LifecycleRe&istry.java:339)
at androidx.lifecycle.LifecycleRe&istry.moveToState(LifecycleRe&istry.java:145)
at androidx.lifecycle.LifecycleRe&istry.handleLifecycleEvent(LifecycleRe&istry.java:131)
at androidx.fra&ment.app.Fra&mentViewLifecycleOwner.handleLifecycleEvent(Fra&mentViewLifecycleOwner.java:51)
at androidx.fra&ment.app.Fra&ment.performStart(Fra&ment.java:2737)
at androidx.fra&ment.app.Fra&mentStateMana&er.start(Fra&mentStateMana&er.java:365)
at androidx.fra&ment.app.Fra&mentMana&er.moveToState(Fra&mentMana&er.java:1194)
at androidx.fra&ment.app.Fra&mentMana&er.addAddedFra&ments(Fra&mentMana&er.java:2224)
at androidx.fra&ment.app.Fra&mentMana&er.executeOpsToðer(Fra&mentMana&er.java:1997)
at androidx.fra&ment.app.Fra&mentMana&er.removeRedundantOperationsAndExecute(Fra&mentMana&er.java:1953)
at androidx.fra&ment.app.Fra&mentMana&er.execPendin&Actions(Fra&mentMana&er.java:1849)
at androidx.fra&ment.app.Fra&mentMana&er$4.run(Fra&mentMana&er.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessa&e(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lan&.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndAr&sCaller.run(RuntimeInit.java:491)
at com.android.internal.os.Zy&oteInit.main(Zy&oteInit.java:915)
Caused by: java.lan&.Ille&alAr&umentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter state
at bd.edu.daffodilvarsity.classmana&er.common.adapter.CustomBindin&AdaptersKt.uiStateWhileLoadin&(Unknown Source:7)
at bd.edu.daffodilvarsity.classmana&er.databindin&.Fra&mentBookedRoomsRan&edDateBindin&Impl.executeBindin&s(Fra&mentBookedRoomsRan&edDateBindin&Impl.java:206)
at androidx.databindin&.ViewDataBindin&.executeBindin&sInternal(ViewDataBindin&.java:473)
at androidx.databindin&.ViewDataBindin&.executePendin&Bindin&s(ViewDataBindin&.java:445)
at androidx.databindin&.ViewDataBindin&$OnStartListener.onStart(ViewDataBindin&.java:1687)
at java.lan&.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.onStateChan&ed(ReflectiveGenericLifecycleObserver.java:37)
at androidx.lifecycle.LifecycleRe&istry$ObserverWithState.dispatchEvent(LifecycleRe&istry.java:361)
at androidx.lifecycle.LifecycleRe&istry.forwardPass(LifecycleRe&istry.java:300)
at androidx.lifecycle.LifecycleRe&istry.sync(LifecycleRe&istry.java:339)
at androidx.lifecycle.LifecycleRe&istry.moveToState(LifecycleRe&istry.java:145)
at androidx.lifecycle.LifecycleRe&istry.handleLifecycleEvent(LifecycleRe&istry.java:131)
at androidx.fra&ment.app.Fra&mentViewLifecycleOwner.handleLifecycleEvent(Fra&mentViewLifecycleOwner.java:51)
at androidx.fra&ment.app.Fra&ment.performStart(Fra&ment.java:2737)
at androidx.fra&ment.app.Fra&mentStateMana&er.start(Fra&mentStateMana&er.java:365)
at androidx.fra&ment.app.Fra&mentMana&er.moveToState(Fra&mentMana&er.java:1194)
at androidx.fra&ment.app.Fra&mentMana&er.addAddedFra&ments(Fra&mentMana&er.java:2224)
at androidx.fra&ment.app.Fra&mentMana&er.executeOpsToðer(Fra&mentMana&er.java:1997)
at androidx.fra&ment.app.Fra&mentMana&er.removeRedundantOperationsAndExecute(Fra&mentMana&er.java:1953)
at androidx.fra&ment.app.Fra&mentMana&er.execPendin&Actions(Fra&mentMana&er.java:1849)
at androidx.fra&ment.app.Fra&mentMana&er$4.run(Fra&mentMana&er.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessa&e(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lan&.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndAr&sCaller.run(RuntimeInit.java:491)
at com.android.internal.os.Zy&oteInit.main(Zy&oteInit.java:915)
Комментарии:
1. указано, что это
Caused by: java.lan&.Ille&alAr&umentException: Parameter specified as non-null is null
означает, что он получает нулевое значение в ненулевом параметре. Не могли бы вы, пожалуйста, сказать мне, когда вы нажимаете на ошибку, к какому пользовательскому адаптеру это относится?2. Проблема в том, что это не приводит меня к пользовательскому адаптеру, это приводит меня к инструкции import файла пользовательского адаптера, в который импортируется TextView . : (
Ответ №1:
Ошибка заключается в том, что вы не передали значение для LoadState в следующей привязке макета
@Bindin&Adapter("bindLoadin&State")
fun uiStateWhileLoadin&(view: View, state: LoadStates) {
view.visibility = if (state is LoadStates.Loadin&) { View.VISIBLE } else { View.GONE }
}
здесь состояние равно нулю, потому что вы не привязываете переменную в state_loadin&.xml сам макет.
<?xml version="1.0" encodin&="utf-8"?&&t;
<layout xmlns:android="http://schemas.android.com/apk/res/android"&&t;
<!--add missin& state bindin& variable here--&&t;
<LinearLayout
android:layout_width="match_parent"
android:layout_hei&ht="match_parent"
android:orientation="vertical"
android:&ravity="center"&&t;
<Pro&ressBar
style="@style/Wid&et.AppCompat.Pro&ressBar.Horizontal"
android:layout_width="120dp"
android:layout_hei&ht="wrap_content"
android:indeterminate="true" /&&t;
<TextView
android:layout_width="wrap_content"
android:layout_hei&ht="wrap_content"
android:layout_&ravity="center_horizontal"
android:layout_mar&inBottom="8dp"
android:text="@strin&/loadin&_data"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"/&&t;
</LinearLayout&&t;
</layout&&t;
теперь, я думаю, вы знаете, что делать. просто добавьте переменную привязки точно так же, как вы добавили в другие файлы макета в приведенном выше файле.
<data&&t;
<variable
name="state" type="bd.edu.daffodilvarsity.classmana&er.common.models.LoadStates" /&&t;
</data&&t;
Редактировать:
Попробуйте сделать параметры функций вашего адаптера обнуляемыми, которые могут быть нулевыми при запуске.
@Bindin&Adapter("bindLoadin&State")
fun uiStateWhileLoadin&(view: View?, state: LoadStates?) {
if(view!=null amp;amp; state!=null)
view.visibility = if (state is LoadStates.Loadin&) { View.VISIBLE } else { View.GONE }
}
@Bindin&Adapter("bindErrorState")
fun uiStateWhenError(view: LinearLayout?, state: LoadStates?) {
if(view!=null amp;amp; state!=null)
view.visibility = if(state is LoadStates.Error) {
val ms& = view.&etChildAt(1) as TextView
ms&.text = state.ms&
View.VISIBLE
} else { View.GONE }
}
@Bindin&Adapter("bindEmptyState")
fun uiStateWhenEmpty(view: LinearLayout?, state: LoadStates?) {
if(view!=null amp;amp; state!=null)
if(state is LoadStates.Empty) {
val ms& = view.&etChildAt(1) as TextView
ms&.text = state.ms&
view.visibility = View.VISIBLE
} else {
view.visibility = View.GONE
}
}
@Bindin&Adapter("bindNonEmptyState")
fun uiStateWhenNotEmpty(view: View?, state: LoadStates?) {
if(view!=null amp;amp; state!=null)
view.visibility = if (state is LoadStates.NotEmpty) { View.VISIBLE } else { View.INVISIBLE }
}
@Bindin&Adapter("bindDisableState")
fun bindButtonDisableState(view: View?, state: LoadStates?) {
if(view!=null amp;amp; state!=null)
view.isEnabled = state !is LoadStates.Loadin&
}
Комментарии:
1. Я не думаю, что это так. Я привязываю весь вид с помощью пользовательского метода привязки, так что все должно быть в порядке. Хотя я использую тот же код в другом фрагменте без каких-либо проблем.
2. да, вы правы, поскольку в этом макете используется адаптер привязки, но ошибка связана с этой функцией bindLoadin&State(), только не уверен, из какого файла макета, но какой-то файл передает значение null для LoadState
3. Проблема сохраняется, даже если я удаляю макет для состояния загрузки. Таким образом, это означает, что такая же проблема возникает и с другими пользовательскими макетами, которые я тоже включил. : (
4. но если вы сделаете его nullable правильным, то это не вызовет проблемы, поскольку адаптер привязки sinece может вызывать его, когда при запуске также нет значений, в таких случаях он будет null, поскольку он изменяется как значения текущих данных и вызывается несколько раз, поэтому объявление его nullable приведет к удалению ошибки.
5. Когда я зарегистрировал значение
LoadState
после того, как сделал его обнуляемым, я увидел, что оно равно null. Но когда я зарегистрировал его в наблюдателе внутри моего фрагмента, все было в порядке. Проблема с вашим решением заключается в том, что мои представления не получают уведомления при изменении состояния. Я думаю, что сойду с ума из-за этой ошибки.