- 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/
我有一个问题,我想通过其他程序打开 chrome://extensions/页面,例如 cmd.exe 或其他程序。 我们知道,如果我们用chrome.exe打开一个网站,我们可以在cmd.exe中执
当您编写manifest.json 文件时,您必须为内容脚本指定匹配。 http 和 https 工作正常,但如果我尝试包含 chrome://*/* 或其任何变体,我会得到一个我尝试对我的匹配使用无
我真的很困惑我想制作一个可以扩展用户的Google日历的Chrome扩展程序,我应该在Google API下注册哪种程序? 它是Web App吗?但是我不打算让服务器托管任何东西,因为Chrome扩展
我想在带有chrome-extension://URL的iframe上运行内容脚本。我在我的manifest.json文件中添加了一行代码,该行是从http://code.google.com/chr
目前,我正在使用记事本和 chrome 控制台的组合对我的 google-chrome-extensions 进行编码。我 100% 确信有更好的方法来对这些扩展进行编程。人们使用什么环境? 最佳答案
在编写 manifest.json 文件时,必须指定 matches用于您的内容脚本。 http和 https工作正常,但如果我尝试包含 chrome://*/*或它的任何变体,我收到一个错误,提示我
关闭。这个问题需要details or clarity .它目前不接受答案。 想改进这个问题吗? 通过 editing this post 添加细节并澄清问题. 关闭 7 年前。 Improve
在发布更新后,我正在尝试为我的 Chrome 扩展程序的用户创造流畅的体验。 我在更新应用程序时重新注入(inject)了我的内容脚本,即使用户继续在扩展更新后未刷新的页面上使用我的扩展,我的功能仍然
将扩展程序从 Chrome 移植到 FF 遵循本教程(在 Chrome 中运行良好):http://www.codingscripts.com/check-whether-user-has-a-chr
我正在将 google-chrome 扩展改编成 firefox。 这个扩展相当简单,它只是重新加载当前浏览器窗口并在其中放置一个特定的字符串(它用于在 Odoo 上激活调试状态)。 但是,当我在 m
我正在尝试在普通 HTML 页面(非扩展)中链接到 chrome://extensions。但是单击链接不会执行任何操作: chrome://extensions 右键单击并在新选项卡中打开只会打开
为 String 编写扩展名很容易,但问题是它总是显示为 "MyString".ExtensionMethod() 如果这样写: public static class Extensions{
如题。我正在运行 Joomla 2.5。 “扩展”下拉菜单中唯一可见的项目是: 模块经理 插件管理器 模板管理器 语言经理 编辑:我这样做是为了安装模板,按照此页面上的说明:http://docs.j
基本上我希望文件名以扩展名列表中的扩展名结尾。这是我在 python 中的代码。我已经将一些示例文件名作为列表,如下所示: extensions = ['.mp3','.m4a','.wma'] fi
在 background.html : chrome.tabs.query({active:true, currentWindow:true},function(tabs){ chrome.tab
我有一个可能被用户禁用的 chrome 扩展。在这种情况下,我想创建一个指向 chrome://extensions 菜单的链接。它会是这样的 Chrome extensions 这是不允许的:不允许
我查看了 Google 文档,但不知道如何更改其类型。 这是我加载时遇到的错误。 尝试安装此扩展时出现警告:“browser_action”仅允许用于扩展程序,这是一个旧版打包应用程序。 这是我的ma
我有一个正在构建的 chrome 扩展,它使用 OAuth 访问许多 API。我没有将我的消费者 secret 存储在扩展程序中,而是重定向到获取 token 的服务器,然后重定向回我的扩展程序中的页
我有一个正在构建的 chrome 扩展,它使用 OAuth 访问许多 API。我没有将我的消费者 secret 存储在扩展程序中,而是重定向到获取 token 的服务器,然后重定向回我的扩展程序中的页
这个问题已经有答案了: Why would a developer place a forward slash at the start of each relative path? (4 个回答)
我是一名优秀的程序员,十分优秀!