Kotlin Android扩展视图背后的编辑魔法是什么?
当我将表单kotlinx.android.synthetic.main.<layout-name>.view.*
的导入添加到Kotlin源代码中时,它会改变Android Studio编辑器的行为。具体而言,它现在认为View
类型的任何属性都具有与分配了ID的布局文件中的每个视图相对应的属性。我假设我在而不是和Activity
或Fragment
这样的课程中这样做。Kotlin Android扩展视图背后的编辑魔法是什么?
例如,假设我在源文件中声明了一个ViewHolder
,并且我向其添加了一个名为x
的属性,其类型为View
。此外,假设我有一个名为item_test
的布局,其中有三个视图声明已分配了ID,a
,b
和c
。当我添加一个布局名称item_test
突然x
具有三个新的属性,a
,b
,c
上述表格的合成输入。或者,更正确地说,编辑器使其看起来好像x
具有这些属性。
经过一番研究,我得出以下结论:
添加布局视图的id作为属性是盲目进行。任何由此类合成导入暗示的视图ID都将添加到类型为
View
(或View
的子类)的任何属性。这包括由类继承的这种类型的属性。由于盲目添加属性,开发人员有义务确保在合成属性被访问之前分配对应于合成导入的运行时视图,否则将抛出异常。
如果在一个文件中指定了两个或多个这样的导入,那么他们的视图id的联合将被盲目地添加到类型为
View
的所有类属性。允许多次导入的目的是解释一个布局文件包含另一个布局文件的情况。
这些结论是否正确?
实现此功能还有其他有趣的细微之处吗?
我正在使用kotlin-gradle-plugin的1.1.2-5版本。
如果我理解正确的,我们假定你是以下几点:
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
}
}
所以,在我看来,这是类型查看任何属性将有一个合成导入相关的综合性能,无论是否它是否有效。因此,扩展实现看起来是蛮力的,如果有什么错误的话,不会提醒开发人员。
这是正确的。现在
,所述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
一个扩展函数时,不存在完整性检查,以检查是否存在于视图层次item_test
x
或y
,就像他们不存在findViewById
一样。如果findViewById
呼叫返回null
,则值为null
或KotlinNullPointerException
被引发。
- 加法布局视图id的作为属性被盲目地进行。任何由这样的综合导入暗示的视图id都被添加到View类型的任何属性(或View的子类)中。这包括由类继承的这种类型的属性。
是,由于属性是基于View
一个扩展特性,如上所述。
- 由于属性被盲目地加入它是在所述显影剂有责任确保相应于合成进口运行时视图被访问的合成属性之前或分配一个异常将被抛出。
我不确定你在问什么。如果你的意思是你在调用x.item_test
之前必须初始化x
,那就对了。另外,x
的层次结构中应该有一个视图item_test
。
- 如果在一个文件中指定了两个或多个这样的导入,那么它们的view id的联合会被盲目地添加到View类型的所有类属性中。 允许多次导入的目的是解释一个布局文件包含另一个布局文件的情况。
是的。
[1]:其实,在幕后这些查找被缓存。请参阅文档中的Under the hood。
@roobyroo我对你的问题作了另一种解释,这是否更好? – nhaarman
我已经按照承诺回复了这个问题。 – roobyroo
@roobyroo请参阅答案的补充。 – nhaarman