Невозможно привязать пользовательский адаптер к макетам с помощью тега include

#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&ether(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&ether(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. Но когда я зарегистрировал его в наблюдателе внутри моего фрагмента, все было в порядке. Проблема с вашим решением заключается в том, что мои представления не получают уведомления при изменении состояния. Я думаю, что сойду с ума из-за этой ошибки.