2

当我将表单的导入添加kotlinx.android.synthetic.main.<layout-name>.view.*到 Kotlin 源时,它会改变 Android Studio 编辑器的行为。具体来说,它现在认为任何类型View的属性都具有对应于布局文件中分配了 id 的每个视图的属性。我假设我在一个不是 or 的课程中​​这样ActivityFragment

例如,假设我ViewHolder在源文件中声明了一个,我向它添加了一个名为xtype的属性View。此外,假设我有一个名为的布局item_test,它具有三个视图声明,它们被分配了 id、abc。当我添加上述表单的合成导入时,其布局名称item_test突然x具有三个新属性abc。或者,更准确地说,编辑器使它看起来好像x具有这些属性。

经过一番研究,我得出以下结论:

  • 将布局视图 ID 添加为属性是盲目的。这种合成导入所隐含的任何视图 id 都将添加到类型View(或 的子类View)的类的任何属性中。这包括类继承的这种类型的属性。

  • 由于属性是盲目添加的,因此开发人员有责任确保在访问合成属性之前分配与合成导入对应的运行时视图,否则将引发异常。

  • 如果在一个文件中指定了两个或多个这样的导入,那么它们的视图 ID 的并集将被盲目地添加到 type 的所有类属性中View

  • 允许多个导入的目的是考虑一个布局文件包含另一个布局文件的情况。

这些结论正确吗?

围绕此功能的实现还有其他有趣的细微之处吗?

我正在使用 kotlin-gradle-plugin 的 1.1.2-5 版本。

4

1 回答 1

4

如果我理解正确,您假设以下内容:

import kotlinx.android.synthetic.main.activity_main.item_test
import kotlinx.android.synthetic.main.activity_main.item_test_2

class MyClass {
  lateinit var x: View
  lateinit var y: View

  fun foo() {
    val foo1 = x.item_test // Compiles
    val foo2 = y.item_test // Compiles as well

    val bar1 = x.item_test_2 // Compiles too
    val bar2 = y.item_test_2 // Compiles yet again
  }
}

因此,在我看来,任何 View 类型的属性都将具有与合成导入关联的合成属性,无论它是否有效。因此,扩展实现似乎是蛮力的,如果有任何问题,也不会向开发人员报告。

那是对的。
现在,item_test导入本质上是View类的扩展属性(简化示例[1]):

val View.item_test get() = findViewById(R.id.item_test)

对于布局中的每个视图,插件都会生成这些属性,并将它们放在它们的相关文件中。这些性质也存在于Activity和上Fragment
因此,当您调用 时x.item_test,您实际上是在调用x.findViewById(R.id.item_test)[1]
编译后,插件会将这些调用替换回findViewById. 所以上述程序[1]的简化反编译版本(Java):

final class MyClass {
   public View x;
   public View y;

   public final void foo() {
      TextView foo1 = (TextView) x.findViewById(id.item_test);
      TextView foo2 = (TextView) y.findViewById(id.item_test);
      TextView bar1 = (TextView) x.findViewById(id.item_test_2);
      TextView bar2 = (TextView) y.findViewById(id.item_test_2);
   }
}

由于这只是 上的扩展功能View,因此没有健全性检查来检查 or 的视图层次结构中是否存在item_test,就像它们不存在一样。如果调用返回,则值 is或 a被抛出。xyfindViewByIdfindViewByIdnullnullKotlinNullPointerException


  • 将布局视图 ID 添加为属性是盲目的。这种合成导入所隐含的任何视图 id 都将添加到视图类型(或视图的子类)的类的任何属性中。这包括类继承的这种类型的属性。

是的,因为该属性是 上的扩展属性View,如上所述。

  • 由于属性是盲目添加的,因此开发人员有责任确保在访问合成属性之前分配与合成导入对应的运行时视图,否则将引发异常。

我不确定你在这里问什么。如果您的意思是您必须x在调用之前进行初始化x.item_test,那就对了。此外,x应该item_test在其层次结构中有一个视图。

  • 如果在一个文件中指定了两个或多个此类导入,则它们的视图 ID 的并集将被盲目添加到 View 类型的所有类属性中。允许多个导入的目的是考虑一个布局文件包含另一个布局文件的情况。

是的。


[1]:实际上,这些查找在幕后被缓存。请参阅文档中的幕后花絮

于 2017-06-21T21:48:27.727 回答