- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
当我添加 kotlinx.android.synthetic.main.<layout-name>.view.*
形式的导入时对于 Kotlin 源代码,它会改变 Android Studio 编辑器的行为。具体来说,它现在考虑 View
类型的任何属性具有与分配了 id 的布局文件中的每个 View 相对应的属性。我假设我在一个不是 Activity
的类(class)里做这件事或 Fragment
.
例如,假设我有一个 ViewHolder
在源文件中声明并向其添加一个名为 x
的属性类型 View
.此外,假设我有一个名为 item_test
的布局具有三个已分配 ID 的 View 声明,a
, b
, 和 c
.当我使用 item_test
的 layout-name 添加上述表单的合成导入时突然x
具有三个新属性,a
, b
, 和 c
.或者,更准确地说,编辑器使它看起来像x
。具有这些属性。
经过一番研究,我得出以下结论:
添加布局 View ID 作为属性是盲目的。此类合成导入所暗示的任何 View ID 都将添加到类型为 View
的类的任何属性中。 (或 View
的子类)。这包括类继承的此类属性。
由于属性是盲目添加的,因此开发人员有责任确保在访问合成属性之前分配对应于合成导入的运行时 View ,否则将抛出异常。
<如果在一个文件中指定了两个或多个这样的导入,那么它们的 View ID 的并集将被盲目地添加到类型为 View
的所有类属性中。 .
允许多次导入的目的是考虑到一个布局文件包含另一个布局文件的情况。
这些结论正确吗?
围绕此功能的实现是否还有其他有趣的微妙之处?
我使用的是 1.1.2-5 版的 kotlin-gradle-plugin。
最佳答案
如果我没理解错的话,你假设如下:
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
}
}
So it appears to me that any property that is of type View will have the synthetic properties associated with a synthetic import regardless of whether it is valid or not. So the extension implementation appears to be brute force and will not tip-off the developer if anything is wrong.
没错。
现在,item_test
导入本质上是 View
类的扩展属性(简化示例[1]):
val View.item_test get() = findViewById(R.id.item_test)
对于布局中的每个 View ,插件都会生成这些属性,并将它们放入它们的相关文件中。 Activity
和 Fragment
也存在这些属性。
因此,当您调用 x.item_test
时,实际上是在调用 x.findViewById(R.id.item_test)
[1]。< br/>编译后,插件会将这些调用替换回 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
的 View 层次结构中或 y
,就像它们不存在于 findViewById
一样。如果 findViewById
调用返回 null
,则值为 null
或抛出 KotlinNullPointerException
。
- The addition of layout view ids as properties is done blindly. Any view id implied by a such a synthetic import is added to any property of the class that is of type View (or a subclass of View). This includes properties of such type inherited by the class.
是的,因为该属性是 View
上的扩展属性,如上所述。
- Because the properties are added blindly it is incumbent upon the developer to ensure that the runtime view corresponding to the synthetic import is assigned before the synthetic properties are accessed or an exception will be thrown.
我不确定你在这里问什么。如果您的意思是您必须在调用 x.item_test
之前初始化 x
,那就对了。此外,x
在其层次结构中应该有一个 View item_test
。
是的。
[1]:实际上,这些查找在幕后被缓存了。参见 Under the hood在文档中。
关于android - Kotlin Android Extensions for Views 背后的编辑器魔法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44685129/
我正在寻找匹配 /(?=\W)(gimme)(?=\W)/gi 或类似的东西。 \W 应该是零宽度字符来包围我的实际匹配项。 也许有一些背景。我想用添加的文字填充替换某些单词(总是 \w+),但前提是
如何在不使用 Intent 连接到 VPN 服务的情况下以编程方式检测流量是否正在通过 VPN。有系统调用吗? 最佳答案 这个有效: private boolean checkVPN() {
我是一名优秀的程序员,十分优秀!