Привязка данных в пользовательских представлениях "Невозможно ссылаться на .setTag для нулевого ссылочного объекта" - PullRequest
0 голосов
/ 12 декабря 2018

Я столкнулся с проблемой, над которой я так долго работал, по крайней мере, 2 недели, и я был настолько ошеломлен, что через столько лет я как-то забыл, как работает привязка данных и как правильно настроить ее для "ПОЛНЫХ ВИДОВ",Я решил проверить это на очень простом проекте, чтобы изолировать его от моего текущего проекта.Очень простое приложение HelloWorld, которое в основном выводит Hello World на экран, используя привязку данных.Проект содержит следующие файлы:

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
        setContentView(binding.root)

        binding.message = "Hello World!"
    }
}

activity_main.xml

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

    <data>
        <variable name="message" type="String" />
    </data>

    <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">

        <com.neonapps.android.sample.databinding.CustomView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"


            <!-- Please take note I am data binding on my custom view -->
            app:message="@{message}"



            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </android.support.constraint.ConstraintLayout>

</layout>

А теперь вот самая важная часть проблемы.Это пользовательское представление CustomView. Я хочу связать определенные данные 'String' с этим представлением, поэтому оно может выводить "Hello World" в этом CustomView:

class CustomView(context : Context, attrs : AttributeSet, defStyleAttrs : Int, defStylRes : Int) : RelativeLayout(context){

    constructor(context : Context, attrs : AttributeSet) : this(context, attrs, 0, 0)

    constructor(context : Context, attrs : AttributeSet, defStyleAttrs : Int) : this(context, attrs, defStyleAttrs, 0)

    private var myMessage : String? = null
        set(value){
            value.let{
                field = it
                binding.message = field
            }
        }

    private val binding : LayoutCustomViewBinding = LayoutCustomViewBinding.inflate(LayoutInflater.from(context), this, true)

    init {
        binding.message?.let{
            binding.message = it
        }
    }

    fun setMessage(message : String?){
        myMessage = message
    }
}

@BindingAdapter(value = ["message"])
fun setMessage(view : TextView, message : String?)
{
    message?.let{
        view.text = it
    }
}

@BindingAdapter(value = ["message"])
fun setMessage(view : CustomView, message : String?)
{
    message?.let{
        view.message = it
    }
}

Здесь подвох.Это CustomView надувает представление, которое можно связать :

<?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="message" type="String" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:message="@{message}"
            tools:text="Hello World"/>

    </RelativeLayout>

</layout>

Так что я в основном привязываю a Строку к этому пользовательскому представлению (которое состоит (предположительно) из многих представлений)в его макете), как только я установил его снаружи, как activity_main.xml выше.

activity_main.kt

<layout
    ...>

    <data>
        ...
    </data>

    <android.support.constraint.ConstraintLayout
            ...>

        <com.neonapps.android.sample.databinding.CustomView
            ...


            <!-- Please take note I am data binding on my custom view -->
            app:message="@{message}"



            .../>

    </android.support.constraint.ConstraintLayout>

</layout>

Как только я собрал весь проект, кажется, все работает нормально.Теперь я запускаю приложение и получаю следующую ошибку:

Attempt to invoke virtual method 'void ******.databinding.CustomView.setTag(java.lang.Object)' on a null object reference
        at com.neonapps.android.sample.databinding.databinding.ActivityMainBindingImpl.<init>(ActivityMainBindingImpl.java:37)
        at com.neonapps.android.sample.databinding.databinding.ActivityMainBindingImpl.<init>(ActivityMainBindingImpl.java:29)
        at com.neonapps.android.sample.databinding.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:44)
        at android.databinding.MergedDataBinderMapper.getDataBinder(MergedDataBinderMapper.java:74)
        at android.databinding.DataBindingUtil.bind(DataBindingUtil.java:199)
        at android.databinding.DataBindingUtil.inflate(DataBindingUtil.java:130)
        at com.neonapps.android.sample.databinding.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:49)
        at com.neonapps.android.sample.databinding.databinding.ActivityMainBinding.inflate(ActivityMainBinding.java:43)
        at *****.MainActivity.onCreate(MainActivity.kt:12)
        at android.app.Activity.performCreate(Activity.java:6904)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1136)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3266)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3415) 
        at android.app.ActivityThread.access$1100(ActivityThread.java:229) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:148) 
        at android.app.ActivityThread.main(ActivityThread.java:7406) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

Мое приложение неожиданно падает, я внезапно впал в панику.Я просто больше не знаю основ DataBinding.Это прекрасно работает, когда я просто привязываю данные к представлениям, но мне не везет на связывание данных для моих собственных пользовательских представлений. Единственное, что сводит меня с ума, это то, что это приводит к сбою в коде, который генерируется автоматически.Я абсолютно не представляю, как он сгенерировал код, ссылающийся на ноль, который он сгенерировал, не присвоив ему ссылкуЯ сдаюсь, есть кое-что, что я сильно упустил.

Я определенно что-то упустил, и я не могу этого заметить.Я продолжал ссылаться на документы библиотеки DataBinding, но мне ничего не помогло.

Я пробовал этот код на

Android Studio: 3.4 Canary 7
Kotlin: 1.3.11

Android Studio: 3.2.1
Kotlin: 1.2.71

Сначала я подумал, что это может быть проблема, связанная с Kotlin / Build config / gradleДо тех пор, пока я не построю этот проект в стабильных средах, и они будут вести себя одинаково независимо.

Это мое проклятие.Любая помощь, чтобы уменьшить мои страдания будет принята с благодарностью!

1 Ответ

0 голосов
/ 16 декабря 2018

Это мое проклятие.Буду признателен за любую помощь, чтобы уменьшить мои страдания!

Я попробую.

Удалите два написанных вами BindingAdapter и перепишите свой класс следующим образом:

class CustomView : RelativeLayout {

    constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0, 0)
    constructor(context: Context, attrs: AttributeSet, defStyleAttrs: Int) :
            this(context, attrs, defStyleAttrs, 0)
    constructor(context: Context, attrs: AttributeSet, defStyleAttrs: Int, defStylRes: Int) :
            super(context, attrs, defStyleAttrs, defStylRes)

    private val binding: LayoutCustomViewBinding = LayoutCustomViewBinding.inflate(LayoutInflater.from(context), this, true)

    var message: String? = null
        set(value) {
            binding.message = value
        }
}

Замените часть app:message="@{message}" в макете вашего CustomView на android:text="@{message}":

<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="message" type="String" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/textview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{message}"
            tools:text="Hello World"/>

    </RelativeLayout>

</layout>

Объяснение:

Вам не понадобятся BindingAdapters, которые вы написали в качестве библиотеки привязки данных автоматическиобнаруживает метод setMessage (), который генерируется из поля сообщения в вашем классе Kotlin во время компиляции.То же самое относится к TextView, библиотека привязки данных обнаружит, что есть метод setText (), и использует его для android:text="@{message}".

...