Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译] 编写AndroidStudio插件(三): 更多配置 #52

Open
qingmei2 opened this issue Jan 21, 2021 · 0 comments
Open

[译] 编写AndroidStudio插件(三): 更多配置 #52

qingmei2 opened this issue Jan 21, 2021 · 0 comments

Comments

@qingmei2
Copy link
Owner

qingmei2 commented Jan 21, 2021

[译] 编写AndroidStudio插件(三):设置页

原文:Write an Android Studio Plugin Part 3: Settings
作者:Marcos Holgado
译者:却把清梅嗅
《编写AndroidStudio插件》系列是 IntelliJ IDEA 官方推荐的学习IDE插件开发的博客专栏,希望对有需要的读者有所帮助。

在本系列的第二部分中,我们学习了如何使用Component对数据进行持久化,以及通过这些数据,在用户更新我们的插件后展示更新了哪些新功能。在今天的文章中,我们将看到如何使用持久化的数据来创建设置页面。

请记住,您可以在GitHub上找到本系列的所有代码,还可以在对应的分支上查看每篇文章的相关代码,本文的代码在Part3分支中。

https://github.com/marcosholgado/plugin-medium

我们要做什么?

本文的目的是为我们的插件创建一个 设置页面,这将是我们迈向将JIRA搬运过来的第一步。我们的设置页面上只会有一个用户名和密码字段,我们的插件将使用该用户名和密码字段与Jira API进行交互。我们还希望能够为 每个项目 分别配不同的设置,从而允许用户根据项目使用不同的Jira帐户(这可能很有用)。

第一步:新建一个Project级别的Component

在本系列的第二部分中,我们已经了解了什么是Component,并且还了解了存在三种不同类型的Component。 因为我们希望能够根据我们的Android Studio的各Project进行不同的设置,因此显而易见的选择是创建一个新的Project Component

我们基本上是在复制和粘贴我们先前创建的Component,但是删除了所有不必要的方法并添加了两个新字段。这些字段将是public的,因为我们将在插件的其它部分中使用它们。

另一处不同是这次我们实现ProjectComponent接口并实现AbstractProjectComponent方法,当然,它的构造方法中也有一个project参数。最后,我们有一个companion object,通过一个project参数,以获取我们的JiraComponent的实例。这将使我们能够从插件中其他位置访问存储的数据。新的JiraComponent看起来像这样:

@State(name = "JiraConfiguration",
        storages = [Storage(value = "jiraConfiguration.xml")])
class JiraComponent(project: Project? = null) :
        AbstractProjectComponent(project),
        Serializable,
        PersistentStateComponent<JiraComponent> {

    var username: String = ""
    var password: String = ""

    override fun getState(): JiraComponent? = this

    override fun loadState(state: JiraComponent) =
            XmlSerializerUtil.copyBean(state, this)

    companion object {
        fun getInstance(project: Project): JiraComponent =
                project.getComponent(JiraComponent::class.java)
    }
}

如我们在上文所做的一样,我们还必须在plugin.xml文件中注册Component

<project-components>
    <!-- Add your project components here -->
    <component>
        <implementation-class>
            components.JiraComponent
        </implementation-class>
    </component>
</project-components>

第二步:UI

在针对我们的设置页面进行下一步之前,我们需要了解如何通过使用Java SwingIntelliJ上创建UIIntelliJ有许多可以使用的Swing组件,以保证插件UIIDE中其它插件保持一致。 但不要被名字中带有Java给欺骗了,因为您仍可将代码转换为Kotlin

创建新GUI(图形用户界面)的一种方法是,只需右键单击并转到New,然后单击GUI Form。该操作将创建一个名为YourName.form的新文件,该文件将链接到另一个名为YourName.java的文件。相比于按照IntelliJ给的编辑器模式进行开发,我更喜欢用我自己的方式,给一个提示:

我将会使用 Eclipse !(欢呼声)

我知道你在想什么,但老实说,它真的很棒。由于一些原因,IntelliJ的编辑器确实很难用,我无法获得预期的效果,但是,如果你对IntelliJ感到满意,请继续使用它!

译者注:我也并不喜欢IDEA官方的编辑器,但也没有很大必要去使用Eclipse,因为使用Eclipse只是对UI预览而已。

回到Eclipse,您可以从这里下载它。 我目前有Oxygen.3a版本,该版本有些旧,但是对于我们要做的并不重要。只需创建一个新项目,然后右键单击NewOther,然后选择JPanel

下图是我们的设置页预览:

接下来我们只需将创建好的源代码从Eclipse复制过来就行了,然后就可以关闭Eclipse了。

回到我们的插件,我们现在将创建一个名为settings的新包和一个名为JiraSettings的新类。 在该类中,我们将创建一个名为createComponent()的新方法,最后我们可以在该方法中粘贴从Eclipse复制的源​​代码。然后是时候将代码转换为Kotlin,您应该也可以自动将其成功转换为Kotlin

完成所有这些操作后,您可能会遇到一些错误,因此请修复它们。

我们需要解决的第一件事是我们的createComponent()方法必须返回一个JComponent,具体原因接下来我们会说到。

因为Eclipse假定我们已经在JPanel中,所以您可以看到很多add方法或似乎并不存在的方法,原因是因为我们不在JPanel中。为解决该问题,我们必须创建一个新的JPanel并给它一些边界(您可以从在Eclipse中创建的JPanel中获取值),并且由于JPanelJComponent的子类,因此我们将在我们的方法中将其返回。

最后,我们只需要进行一些调整就可以编译整个程序,最终效果应该如下:

class JiraSettings {

    private val passwordField = JPasswordField()
    private val txtUsername = JTextField()

    fun createComponent(): JComponent {

        val mainPanel = JPanel()
        mainPanel.setBounds(0, 0, 452, 120)
        mainPanel.layout = null

        val lblUsername = JLabel("Username")
        lblUsername.setBounds(30, 25, 83, 16)
        mainPanel.add(lblUsername)

        val lblPassword = JLabel("Password")
        lblPassword.setBounds(30, 74, 83, 16)
        mainPanel.add(lblPassword)

        passwordField.setBounds(125, 69, 291, 26)
        mainPanel.add(passwordField)

        txtUsername.setBounds(125, 20, 291, 26)
        mainPanel.add(txtUsername)
        txtUsername.columns = 10

        return mainPanel
    }
}

第三步:Extensions 和 Extension points

在继续开发设置页面前,我们必须讨论extensionsextension points。它们将允许您的插件与其他插件或与IDE本身进行交互。

  • 如果要扩展其他插件或IDE的功能,则必须声明一个或多个extensions
  • 如果要让插件允许其他插件扩展其功能,则必须声明一个或多个extension points

因为我们要将设置页面添加到Android StudioPreferences中,所以我们真正要做的是扩展Android Studio的功能,因此我们必须声明一个extensions

为此,我们必须实现Configurable,同时还必须重写一些方法。

  • 幸运的是,我们已经有了createComponent()方法,因此我们只需添加override关键字就可以了。
  • 我们将创建一个booleanmodified,其默认值为false,并作为isModified()的返回值。我们稍后会再讲到这一点,目前它代表了设置页面apply按钮是否被启用。
  • 我们将getDisplayName()的返回值用于展示设置页的名字。
  • apply方法中,我们需要编写将在用户单击Apply时执行的代码。很简单,我们为用户所在的Project获取JiraComponent的实例,然后将UI中的值保存到Component中。最后,我们将Modify设置为false,届时我们要禁用Apply按钮。

最终展示效果如下:

class JiraSettings(private val project: Project): Configurable {

    private val passwordField = JPasswordField()
    private val txtUsername = JTextField()

    private var modified = false

    override fun isModified(): Boolean = modified

    override fun getDisplayName(): String = "MyPlugin Jira"

    override fun apply() {
        val config = JiraComponent.getInstance(project)
        config.username = txtUsername.text
        config.password = String(passwordField.password)

        modified = false
    }

    override fun createComponent(): JComponent {

        val mainPanel = JPanel()
        mainPanel.setBounds(0, 0, 452, 120)
        mainPanel.layout = null

        val lblUsername = JLabel("Username")
        lblUsername.setBounds(30, 25, 83, 16)
        mainPanel.add(lblUsername)

        val lblPassword = JLabel("Password")
        lblPassword.setBounds(30, 74, 83, 16)
        mainPanel.add(lblPassword)

        passwordField.setBounds(125, 69, 291, 26)
        mainPanel.add(passwordField)

        txtUsername.setBounds(125, 20, 291, 26)
        mainPanel.add(txtUsername)
        txtUsername.columns = 10

        return mainPanel
    }
}

第四步:解决最后的问题

我们几乎完成了,只剩下最后几个问题。

首先,我们要保存用户的偏好设置,但目前我们还未对其加载。UI是在createComponent()方法中创建的,因此我们只需要在返回之前添加以下代码,即可使用先前存储的值设置UI:

val config = JiraComponent.getInstance(project)
txtUsername.text = config.username
passwordField.text = config.password

接下来,我们将使用isModified()解决问题。 当用户修改设置页中的任何值时,我们需要以某种方式将值从false更改为true。一种非常简单的方法是实现 DocumentListener,该接口为我们提供了3种方法: changeUpdateinsertUpdateremoveUpdate

在这些方法中,我们唯一要做的就是简单地将Modify的值更改为true,最后将DocumentListener添加到我们的密码和用户名字段中。

override fun changedUpdate(e: DocumentEvent?) {
    modified = true
}

override fun insertUpdate(e: DocumentEvent?) {
    modified = true
}

override fun removeUpdate(e: DocumentEvent?) {
    modified = true
}

最终实现如下:

class JiraSettings(private val project: Project): Configurable, DocumentListener {
    private val passwordField = JPasswordField()
    private val txtUsername = JTextField()
    private var modified = false

    override fun isModified(): Boolean = modified

    override fun getDisplayName(): String = "MyPlugin Jira"

    override fun apply() {
        val config = JiraComponent.getInstance(project)
        config.username = txtUsername.text
        config.password = String(passwordField.password)
        modified = false
    }

    override fun changedUpdate(e: DocumentEvent?) {
        modified = true
    }

    override fun insertUpdate(e: DocumentEvent?) {
        modified = true
    }

    override fun removeUpdate(e: DocumentEvent?) {
        modified = true
    }

    override fun createComponent(): JComponent {

        val mainPanel = JPanel()
        mainPanel.setBounds(0, 0, 452, 120)
        mainPanel.layout = null

        val lblUsername = JLabel("Username")
        lblUsername.setBounds(30, 25, 83, 16)
        mainPanel.add(lblUsername)

        val lblPassword = JLabel("Password")
        lblPassword.setBounds(30, 74, 83, 16)
        mainPanel.add(lblPassword)

        passwordField.setBounds(125, 69, 291, 26)
        mainPanel.add(passwordField)

        txtUsername.setBounds(125, 20, 291, 26)
        mainPanel.add(txtUsername)
        txtUsername.columns = 10

        val config = JiraComponent.getInstance(project)
        txtUsername.text = config.username
        passwordField.text = config.password

        passwordField.document?.addDocumentListener(this)
        txtUsername.document?.addDocumentListener(this)

        return mainPanel
    }
}

第五步:声明 extension

Component相同,我们还必须在plugin.xml文件中声明extension

<extensions defaultExtensionNs="com.intellij">
    <defaultProjectTypeProvider type="Android"/>
    <projectConfigurable
            instance="settings.JiraSettings">
    </projectConfigurable>
</extensions>

大功告成!调试或安装插件时,您可以转到Android Studio中的Preferences/Other Settings,找到新的设置页。您也可以使用不同的Project进行测试,并且每个Project都会记住自身的设置。

这就是第三部分的全部内容。在下一篇文章中,我们将看到如何使用这些设置来创建新的Action,将Jira相关功能迁移过来。同时,如果您有任何疑问,请访问Twitter或发表评论。


《编写AndroidStudio插件》译文系列

关于译者

Hello,我是 却把清梅嗅 ,如果您觉得文章对您有价值,欢迎 ❤️,也欢迎关注我的 博客 或者 GitHub

如果您觉得文章还差了那么点东西,也请通过 关注 督促我写出更好的文章——万一哪天我进步了呢?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant