布局在库模块中时,合成视图的未解析参考

时间:2018-01-22 09:52:54

标签: android kotlin-android-extensions

使用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 tracker

中已注册的问题

10 个答案:

答案 0 :(得分:6)

一种解决方法是创建一个“别名”属性,该属性可以从其他模块中使用:

// 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"

对我们有用。必须为viewfragment等创建不同的扩展名。必须手动进行扩展有点繁琐,但这是我们找到的最简单的解决方法。

附加:这也是一种将合成扩展导出为公共库的一部分的好方法,而不仅仅是在内部模块中。

答案 1 :(得分:3)

以上答案均未对我有帮助。 将这两个插件添加到库模块顶部的build.gradle文件中:

apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'

这应该可以解决问题。

答案 2 :(得分:2)

您可以尝试切换到1.2.30-eap-16并添加

androidExtensions { experimental = true }

build.gradle

答案 3 :(得分:2)

我也遇到了这个问题,我的解决方法是:

  • 请勿使用import kotlinx.android.synthetic.main .... *

  • 使用findViewById()

例如:textview_id.text =“ abc”。我将其更改为findViewById(R.id.textview_id).text =“ abc”

答案 4 :(得分:1)

@cesards上面评论中所述,这是an on going issue与Kotlin Android扩展。直到今天仍未解决。


好:使用自定义视图

我的主要建议是将视图和相关行为封装为自定义视图,而不是通过<include>标签将其包括在内,而应将其直接用作布局<com.example.app.YourCustomView>

无论您的视图类是否在另一个模块中,这都将起作用。


丑陋和丑陋:Id冲突的解决方法

但是,如果您只想从包含的视图中获得一个参考,我发现了一个解决方法。

按照以下步骤将kotlin合成导入用于其他模块中包含的布局:

  1. 在包含的xml文件中为要获取引用的视图提供ID。
  2. 为包含xml文件中的include标记提供相同的ID。
  3. 现在合成从包含的布局(而非包含的布局)中导入视图(id)

我不确定这是如何工作以及为什么起作用,但这是一种非常脆弱和肮脏的重用布局方式。

示例:

您的包含布局(fragment_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 :(得分:1)

该解决方案比想象的要简单得多,Kotlin合成糖是Kotlin Android扩展的一部分,您只需要手动应用插件(Android Studio会自动将此插件仅应用于应用程序模块):

将以下内容添加到库/功能模块的构建Gradle中:

apply plugin: 'kotlin-android-extensions'

现在,以您的Kotlin代码享受Kotlin合成糖! ;)

答案 6 :(得分:0)

前提条件

请勿导入以下库import kotlinx.android.synthetic.main.my_view.view.*

app / 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

来源:

答案 7 :(得分:0)

我的情况是::我需要子类(应用模块)中的基类(来自lib)视图引用,并且具有来自库模块的基类的视图引用最少。

我会说,这是一种解决方法,而不是解决方案。

解决方案1 ​​从基类获取视图引用

//lib module snippet
import kotlinx.android.synthetic.main.view_user_input.*

class LibModuleBaseActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.view_user_input)
   }

    protected val vFirstName: TextView by lazy { tvFirstName }
    ...........
}


//app module snippet
class AppModuleActivity : LibModuleBaseActivity() {

    fun setUserDetails(name: String) {
        vFirstName.text = name
    }
}

解决方案2 通过基本类完成任务

//lib module snippet
import kotlinx.android.synthetic.main.view_user_input.*

class LibModuleBaseActivity : AppCompatActivity() {
    protected fun setUserDetails(name: String) {
        tvFirstName.text = name
    }
    ...........
}


//app module snippet
class AppModuleActivity : LibModuleBaseActivity() {
    ............
    setUserDetails("user_name")
}

注意:仅当您从lib模块继承并且您的Base类正在放大视图时,此方法才有效。

答案 8 :(得分:0)

在上述@pablisco解决方案的逐步改进中,我的库中有一个文件:

// SyntheticExports.kt
@file:Suppress("ClassName")

package com.example.library.synthetic.main

import android.view.TextView
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.common_layout.view.rootLayout as syntheticRootLayout
import kotlinx.android.synthetic.main.common_layout.view.retryButton as syntheticRetryButton

object common_layout {
    object view {
        inline val View.rootLayout: ViewGroup get() = syntheticRootLayout
        inline val View.retryButton: TextView get() = syntheticRetryButton
    }
}

然后在另一个模块上:

// MainActivity.kt
import com.example.library.synthetic.main.common_layout.view.rootLayout
import com.example.library.synthetic.main.common_layout.view.retryButton

rootView.rootLayout.isVisible = true
rootView.retryButton.setOnClickListener { doIt() }

如果此问题已解决,我只需要更改导入以“ kotlinx.android”开头,而不是“ comp.example.library”即可。

答案 9 :(得分:-1)

我通过将androidExtension和buildscript从项目build.gradle复制到使用该库的模块,解决了我的问题。

A = JOIN X by (j1) left outer, Y by (j1);

SPLIT A into B (IF Y::j1 IS NULL), C otherwise;

D = FOREACH B GENERATE X::j2;

注意:我使用以下版本的kotlin和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
}