Original text: Write an Android Studio Plugin Part 3: Settings
Author: Marcos Holgado
Translator: Queba Qingmeixi
"Writing AndroidStudio Plugin" series is a blog column officially recommended by IntelliJ IDEA for learning IDE plugin development. I hope it will be helpful to readers in need.
In the second part of this series , we learned how to use Component
persistent data and use this data to show which new features have been updated after users update our plugin. In today's article, we'll see how to use persistent data to create a settings page.
Remember, you can GitHub
find all the code in this series on , and you can also check out the relevant code for each article on the corresponding branch, where the code for this article is in the Part3
branch.
https://github.com/marcosholgado/plugin-medium
What are we going to do?
The purpose of this article is to create a settings page for our plugin , which will be our JIRA
first step towards bringing it over. There will only be one username and password field on our settings page that our plugin will use to Jira API
interact with . We also want to be able to have different settings for each projectJira
, allowing users to use different accounts based on project (which might be useful).
Step 1: Create a new Project-level Component
In the second part of this series, we've seen what is Component
, and also learned that there are three different types of Component
. Since we want to be able to set things up differently depending on our Android Studio
profile , the obvious choice is to create a new one .Project
Project Component
We're basically copying and pasting what we created earlier Component
, but removing all unnecessary methods and adding two new fields. These fields will be public
as we will be using them in other parts of the plugin.
Another difference is that this time we implement ProjectComponent
the interface and implement AbstractProjectComponent
the method, which of course also has a parameter in its constructor project
. Finally, we have a companion object
, pass a project
parameter to get our JiraComponent
instance of. This will allow us to access stored data from elsewhere in the plugin. The new one JiraComponent
looks like this:
@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)
}
}
As we did above, we must also plugin.xml
register in the file Component
:
<project-components>
<!-- Add your project components here -->
<component>
<implementation-class>
components.JiraComponent
</implementation-class>
</component>
</project-components>
Step 2: UI
Before we can take the next step for our setup page, we need to understand how to create on Java Swing
by using . There are a number of components that can be used to keep the plugin consistent with other plugins in the plugin. But don't be fooled by the suffix in the name, as you can still convert the code to .IntelliJ
UI
IntelliJ
Swing
UI
IDE
Java
Kotlin
One way to create a new GUI
(graphical user interface) is to just right click and go New
, then click GUI Form
. This action will create a YourName.form
new file named , which will link to another YourName.java
file named . Compared with IntelliJ
developing according to the given editor mode, I prefer to use my own way and give a hint:
I'll be using Eclipse! (cheers)
I know what you're thinking, but honestly, it's really good. For some reason, IntelliJ
the editor is really hard to use, and I can't get the desired effect, but, if you are IntelliJ
happy with it, please continue to use it!
Translator's Note : I don't like
IDEA
the official editor, but there is no great need to use itEclipse
, because the useEclipse
is only for UI preview.
Back Eclipse
, you can download it from here . I currently have Oxygen.3a
the version, which is a bit old, but it doesn't matter for what we're going to do. Just create a new project, then right click New
, Other
, and select JPanel
:
The image below is a preview of our settings page:
Eclipse
Next, we only need to copy the created source code from , and then we can close Eclipse
it.
Going back to our plugin, we will now create a settings
new package called and a JiraSettings
new class called . Inside that class, we'll create a createComponent()
new method called , and finally in that method we can paste Eclipse
the source code we copied from . Then it's time to convert the code to Kotlin
, which you should be able to automatically convert successfully to as well Kotlin
.
After doing all this, you may encounter some bugs, so fix them.
The first thing we need to fix is that our createComponent()
method must return one JComponent
, for reasons we'll see next.
Because Eclipse
we're assumed to be in JPanel
, you can see a lot of add
methods or methods that don't seem to exist because we're not JPanel
in. To fix that, we have to create a new one JPanel
and give it some bounds (you can get values from the one Eclipse
created in JPanel
), and since JPanel
it's JComponent
a subclass of , we'll return it in our method.
Finally, we only need to make some adjustments to compile the whole program, and the final effect should be as follows:
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
}
}
Step 3: Extensions and Extension points
Before continuing to develop the settings page, we must discuss extensions
and extension points
. They will allow your plugin IDE
to interact with other plugins or with itself.
- If you want to extend the functionality of other plugins or plugins
IDE
, you must declare one or more of themextensions
. - One or more must be declared if a plugin is to allow other plugins to extend its functionality
extension points
.
Since we're adding the settings page to the Android Studio
, Preferences
what we're really going to do is extend Android Studio
the functionality, so we have to declare one extensions
.
To do this, we have to implement Configurable
and at the same time we have to override some methods.
- Luckily, we already have
createComponent()
the method, so we just need to addoverride
the keywords. - We'll create a
boolean
,modified
with a default value offalse
, asisModified()
the return value of . We'll come back to this later, for now it represents whether the settings pageapply
button is enabled or not. - We use
getDisplayName()
the return value of to display the name of the settings page. - In
apply
the method, we need to writeApply
the code that will be executed when the user clicks. Quite simply, weProject
getJiraComponent
an instance of where the user is, and thenUI
save the value inComponent
to . Finally, we'llModify
set tofalse
, at which point we want to disableApply
the button.
The final display effect is as follows:
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
}
}
Step 4: Solve the final problem
We're almost done, just a few last questions.
First, we want to save the user's preferences, but we haven't loaded them yet. UI
is createComponent()
created in the method, so we just need to add the following code before returning to set it with the previously stored value UI
:
val config = JiraComponent.getInstance(project)
txtUsername.text = config.username
passwordField.text = config.password
Next, we'll isModified()
solve the problem using . false
We need to somehow change the value from to when the user modifies any value in the settings page true
. A very simple way is to implement DocumentListener , which provides us with 3 methods: changeUpdate , insertUpdate and removeUpdate .
In these methods, the only thing we have to do is simply Modify
change the value of to true
, which will finally DocumentListener
be added to our password and username fields.
override fun changedUpdate(e: DocumentEvent?) {
modified = true
}
override fun insertUpdate(e: DocumentEvent?) {
modified = true
}
override fun removeUpdate(e: DocumentEvent?) {
modified = true
}
The final implementation is as follows:
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
}
}
Step 5: Declare the extension
Same as Component
, we also have to plugin.xml
declare it in the file extension
.
<extensions defaultExtensionNs="com.intellij">
<defaultProjectTypeProvider type="Android"/>
<projectConfigurable
instance="settings.JiraSettings">
</projectConfigurable>
</extensions>
You're done! When debugging or installing a plugin, you can find the new settings page Android Studio
by going to . You can also test Preferences/Other Settings
with different ones , and each will remember its own settings.Project
Project
That's all for part three. In the next article, we'll see how to use these settings to create new Action
, Jira
migrated functionality over. In the meantime, if you have any questions, please visit Twitter or leave a comment.
"Writing AndroidStudio Plugins" translation series
- Translation: Writing AndroidStudio plug-ins (1): Create a basic plug-in
- Translation: Writing AndroidStudio plug-ins (2): persistent data
- Translation: Writing AndroidStudio plug-ins (3): Settings page
- Translation: Writing AndroidStudio plug-ins (4): Integrating Jira
- Translation: Writing AndroidStudio plug-ins (5): localization and notifications
About the translator
Hello, I am Qingmei Xun . If you think the article is valuable to you, welcome ❤️, and welcome to follow my blog or GitHub .
If you feel that the article is still lacking, please follow me to urge me to write a better article—what if I improve someday?