Неразрешенная ссылка для синтетического представления, когда макет находится в библиотечном модуле

используя Kotlin 1.20.20 (не важно, старые версии ведут себя одинаково)

Когда макет находится в отдельном библиотечном модуле, Android Studio не обнаруживает проблем и ссылается на представление

import kotlinx.android.synthetic.main.view_user_input.*

Но когда я пытаюсь скомпилировать приложение, он не работает с Unresolved reference: view_user_input на :app:compileDebugKotlin.

Все работает нормально, когда на просмотр ссылается в библиотечном модуле.

Я что-то упустил?

Добавление структуры проекта. Все модули используют kotlin и kotlin-расширения.

build.gradle
app/
  build.gradle //main application
library-module-a/
  build.gradle //library application
library-module-b/
  build.gradle //library application

Вот пример приложения https://github.com/mjurkus/KotlinxTest

Зарегистрированная проблема для этого в трекер KT

Ответ 1

Я также получил эту проблему, и мое решение:

  • Не используйте импорт kotlinx.android.synthetic.main.... *

  • Используйте findViewById()

Пример: textview_id.text = "abc". Я изменяю его на findViewById (R.id.textview_id).text = "abc"

Ответ 2

Один из способов решить эту проблему - создать свойство "псевдоним", которое можно использовать из других модулей:

// SyntheticExports.kt
package com.example.synthetic.exported

import kotlinx.android.synthetic.main.layout_in_library.*

inline val Activity.exported_text_view get() = text_view

Затем на другом модуле:

// MainActivity.kt
import com.example.synthetic.exported.exported_text_view

exported_text_view.text = "example"

Это работает для нас. Нужно создавать различные расширения для view, fragment и т.д. Это немного утомительно, когда приходится делать их вручную, но это самый простой способ, который мы нашли.

Дополнительно: Это также неплохой способ экспортировать синтетические расширения как часть публичной библиотеки, а не только во внутренний модуль.

Ответ 3

вы можете попробовать переключиться на 1.2.30-eap-16 и добавить

androidExtensions { experimental = true }

к вашему build.gradle.

Ответ 4

Как упомянуто в комментарии @cesards выше, это - продолжающаяся проблема с Расширениями Kotlin Android. И это не решено на сегодняшний день.


Хорошо: используйте пользовательские виды

Мое основное предложение заключается в инкапсуляции представления и соответствующего поведения в качестве пользовательского представления, и вместо того, чтобы включать его через <include>, используйте его непосредственно в макете как <com.example.app.YourCustomView>.

Это будет работать независимо от того, находится ли ваш класс просмотра в другом модуле или нет.


Bad and Ugly: Id Collision Обходной путь

Однако я нашел хакерский обходной путь, если вы хотите получить только одну ссылку из включенного представления.

Выполните следующие шаги, чтобы использовать kotlin синтетический импорт для макетов, включенных в другие модули:

  1. Присвойте идентификатор представлению, на которое хотите получить ссылку, во включенном XML файле.
  2. Присвойте тот же идентификатор тегу include во включаемом XML файле.
  3. Теперь синтетически импортируйте представление (id) из включенного макета (не из включенного)

Я не совсем уверен, как и почему это работает, но это очень хрупкий и грязный способ повторного использования макетов.

Пример:

Ваш включающий макет (frag_example.xml)

<include
    android:id="@+id/exampleView"
    layout="@layout/example_layout" />

Ваш включенный макет (example_layout.xml)

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView
        android:id="@+id/exampleView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</merge>

Ваш фрагмент класса (ExampleFragment.kt)

import kotlinx.android.synthetic.main.fragment_example.exampleView

// Do not import the exampleView through example_layout.exampleView

class ExampleFragment : Fragment() {
    // Do something with imported exampleView
}

Ответ 5

Предпосылки

Не импортируйте следующую библиотеку import kotlinx.android.synthetic.main.my_view.view.*

Приложение /MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val view: View = MyView(this)
        view.findViewById<TextView>(R.id.textViewPocLib).text = "I can edit the library components"
        setContentView(view)
}

my_library/MyView.kt

class MyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    LinearLayout(context, attrs, defStyleAttr) {

    init {
        LayoutInflater.from(context)
            .inflate(R.layout.my_view, this, true)
    }
}

GL

Источники:

Ответ 6

Я решил свою проблему с копией androidExtension и buildscript из проекта build.gradle в модуль, который использует библиотеку.

buildscript {

    repositories {
        jcenter()
        google()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:$gradle_version"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

androidExtensions {
    experimental = true
}

Примечание: я использую следующую версию kotlin и gradle:

buildscript {
    ext.kotlin_version = '1.3.0'
    ext.gradle_version = '3.0.1'