🐜 版本信息:
🐞 JDK:1.8+
🐌 IDEA:2019.3
🐝 插件項目基於gradle構建。
🦟 知識背景:swing
🦇 參考文檔:
http://www.jetbrains.org/intellij/sdk/docs/tutorials/build_system/prerequisites.html
http://www.jetbrains.org/intellij/sdk/docs/user_interface_components/tool_windows.html
http://www.jetbrains.org/intellij/sdk/docs/user_interface_components/dialog_wrapper.html
https://intellij-support.jetbrains.com/hc/en-us/community/posts/360003338799-Build-compatible-plugin
目標
本實例實現一個Idea的插件,彈出一個表單Dialog,然后點擊按鈕,獲取表單里輸入的內容,然后將內容打印在表單的上方。
成品圖展示:
圖1
一、項目初始化
新建一個gradle項目,修改其build.gradle文件:
plugins {
id 'java'
id 'org.jetbrains.intellij' version '0.4.14' //引入intellij的gradle插件
}
group 'org.example'
version '1.0' //定義jar包/zip包的版本號
sourceCompatibility = 1.8 //限制jdk的使用版本號,這里限制到8,表示生成的idea插件只能運行在jdk8以上的環境中
repositories {
mavenCentral() //遠程倉庫
}
dependencies {
//這里引別的依賴包
testCompile group: 'junit', name: 'junit', version: '4.12'
}
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
// 這里是指打插件包的時候用idea什么版本的依賴包打
// 比如這里用2019.3打包,如果你的插件實現源碼里用了2019.3不存在的依賴包或類,就會報錯
// 一般就填當前IDEA的版本號即可
version "2019.3"
}
patchPluginXml {
//changeNotes里的內容展示位置參考圖14
changeNotes """
1.0版本.
第1.0版本:初始化這個測試插件項目"""
// 這個意思是說當前定義的這個插件最早支持到什么版本的IDEA
// 這里配置sinceBuild=191,表示插件只能被版本號大於等於2019.1版本的IDEA安裝,低於這個版本的將拋無法兼容的錯誤
// ↑上方參考這篇問答:https://intellij-support.jetbrains.com/hc/en-us/community/posts/360003338799-Build-compatible-plugin
sinceBuild "191"
}
然后Idea的右邊欄gradle將會多出intellij選項:
圖2
這里說下runIde,它用來調試插件,運行它會再次啟動一個Idea,這個Idea會自動安裝上你當前定義的插件包,讓你用來調試。
二、新增plugin.xml
這個文件非常重要,它可以指定你定義的插件出現在IDEA的哪個位置,可以指定具體的處理邏輯,還可以定義插件名稱、子名稱等等。
這個文件位於MATE-INF下:
圖3
配置內容為:
<idea-plugin>
<!--插件的id,注意不要跟其他插件重復,這個id全局唯一,盡可能復雜些-->
<id>plugin.test</id>
<!--插件的名稱-->
<name>PluginTest</name>
<vendor email="xxxx@qq.com" url="http://www.bilibili.com">你公司的名字</vendor>
<!--插件的描述信息,支持html,展示的位置參考圖14-->
<description><![CDATA[
Plugin Test<br>
第一行:單純只是個測試<br>
第二行:都說了只是個測試(● ̄(エ) ̄●)<br>
<a href='https://www.bilibili.com'>你猜猜這是哪個網站?</a>
<em>v1.0</em>
]]></description>
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>
<!--跟build.gradle里的sinceBuild一致即可,意義相同,必須配置-->
<idea-version since-build="191"/>
<actions>
<!--下面的group是分組,分組需要有一個唯一的id標識,text用來控制分組出現在IDEA時呈現的文案,description是描述,不會展現出來,簡單描述下分組就行-->
<group id="PluginTest" text="插件測試組" description="插件測試描述">
<!--add-to-group控制把該分組加到IDEA里,group-id用來描述加在哪個位置,MainMenu表示加在IDEA上方的主菜單欄里,
anchor表示順序,last表示最后一個,所以下面的配置可以描述為:將該插件加到IDEA上方主菜單欄的最后一位-->
<add-to-group group-id="MainMenu" anchor="last"/>
<!--這個用來指定一個分組下的觸發動作,同樣的需要一個id,自定義;class就是用來處理這個動作的邏輯類,具體的插件邏輯都會寫到對應的action類里,text用來控制文案,description為描述-->
<action id="Plugin.Test.Action"
class="plugin.test.FromAction"
text="表單測試" description="表單測試描述"/>
</group>
</actions>
</idea-plugin>
然后定義一個Action類,記為FormAction,繼承AnAction,實現其抽象方法actionPerformed即可:
public class FromAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
//TODO 這里放插件邏輯
}
}
三、啟動
現在雙擊runIde即可調出另外一個安裝了這個插件的IDEA界面,然后可以看運行結果進行調試。 runIde還支持debug模式,不過運行時要右擊選擇:
圖4
來看下調試IDEA的界面運行效果:
圖5
四、定義Action
4.1:定義會話框類
經過上面三步的配置,插件的基本樣式已經展示出來,但是點擊下方“表單測試”的action,並沒有什么用,因為其綁定的FormAction類里沒有任何有意義的實現。現在來實現開始的目標,點擊“表單測試”后,彈出一個自定義的表單會話框,然后點擊按鈕,獲取表單內容后打印在會話框內。
會話框(Dialog)需要定義一個繼承了IDEA的DialogWrapper抽象類的子類,這個子類就是自定義的會話框實現,所有的樣式定義、功能觸發都是放到這個子類里的,現定於如下子類:
public class FormTestDialog extends DialogWrapper {
private String projectName; //假如需要獲取到項目名,作為該類的屬性放進來
// DialogWrapper沒有默認的無參構造方法,所以需要重寫構造方法,它提供了很多重載構造方法,
// 這里使用傳project類型參數的那個,通過Project對象可以獲取當前IDEA內打開的項目的一些屬性,
// 比如項目名,項目路徑等
public FormTestDialog(@Nullable Project project) {
super(project);
setTitle("表單測試~~"); // 設置會話框標題
this.projectName = project.getName();
}
// 重寫下面的方法,返回一個自定義的swing樣式,該樣式會展示在會話框的最上方的位置
@Override
protected JComponent createNorthPanel() {
return null;
}
// 重寫下面的方法,返回一個自定義的swing樣式,該樣式會展示在會話框的最下方的位置
@Override
protected JComponent createSouthPanel() {
return null;
}
// 重寫下面的方法,返回一個自定義的swing樣式,該樣式會展示在會話框的中央位置
@Override
protected JComponent createCenterPanel() {
return null;
}
}
4.2:會話框模塊&類元素對照
找個實際的會話框為例,針對上述中幾個方法所控制的會話框里的元素如下:
圖6
4.3:自定義會話框元素
4.3.1:會話框方法重定義
按照本文的實現目標,自定義的表單主體部分可以位於createCenterPanel里,然后表單的大標題可以放到createNorthPanel里,提交按鈕可以放到createSouthPanel里,現在改寫如下:
public class FormTestDialog extends DialogWrapper {
private String projectName;
//swing樣式類,定義在4.3.2
private FormTestSwing formTestSwing = new FormTestSwing();
public FormTestDialog(@Nullable Project project) {
super(true);
setTitle("表單測試~~"); //設置會話框標題
this.projectName = project.getName(); //獲取到當前項目的名稱
init(); //觸發一下init方法,否則swing樣式將無法展示在會話框
}
@Override
protected JComponent createNorthPanel() {
return formTestSwing.initNorth(); //返回位於會話框north位置的swing樣式
}
// 特別說明:不需要展示SouthPanel要重寫返回null,否則IDEA將展示默認的"Cancel"和"OK"按鈕
@Override
protected JComponent createSouthPanel() {
return formTestSwing.initSouth();
}
@Override
protected JComponent createCenterPanel() {
//定義表單的主題,放置到IDEA會話框的中央位置
return formTestSwing.initCenter();
}
}
4.3.2:自定義swing樣式
下面是放置swing樣式的類:
public class FormTestSwing {
private JPanel north = new JPanel();
private JPanel center = new JPanel();
private JPanel south = new JPanel();
//為了讓位於底部的按鈕可以拿到組件內容,這里把表單組件做成類屬性
private JLabel r1 = new JLabel("輸出:");
private JLabel r2 = new JLabel("NULL");
private JLabel name = new JLabel("姓名:");
private JTextField nameContent = new JTextField();
private JLabel age = new JLabel("年齡:");
private JTextField ageContent = new JTextField();
public JPanel initNorth() {
//定義表單的標題部分,放置到IDEA會話框的頂部位置
JLabel title = new JLabel("表單標題");
title.setFont(new Font("微軟雅黑", Font.PLAIN, 26)); //字體樣式
title.setHorizontalAlignment(SwingConstants.CENTER); //水平居中
title.setVerticalAlignment(SwingConstants.CENTER); //垂直居中
north.add(title);
return north;
}
public JPanel initCenter() {
//定義表單的主體部分,放置到IDEA會話框的中央位置
//一個簡單的3行2列的表格布局
center.setLayout(new GridLayout(3, 2));
//row1:按鈕事件觸發后將結果打印在這里
r1.setForeground(new Color(255, 47, 93)); //設置字體顏色
center.add(r1);
r2.setForeground(new Color(139, 181, 20)); //設置字體顏色
center.add(r2);
//row2:姓名+文本框
center.add(name);
center.add(nameContent);
//row3:年齡+文本框
center.add(age);
center.add(ageContent);
return center;
}
public JPanel initSouth() {
//定義表單的提交按鈕,放置到IDEA會話框的底部位置
JButton submit = new JButton("提交");
submit.setHorizontalAlignment(SwingConstants.CENTER); //水平居中
submit.setVerticalAlignment(SwingConstants.CENTER); //垂直居中
south.add(submit);
return south;
}
}
現在點擊下runIde按鈕,同樣的,在調試IDE里點擊“表單測試”,然后就會彈出如下表單框:
圖7
🌿 除非有特殊情況需要自定義swing樣式,否則建議不加任何swing樣式,這樣自定義的swing界面是會隨着IDEA的主題改變而去自適應的,比如將圖7中的調試IDE的主題設置成Darcula,自定義的表單也會自適應的變成黑色背景:
圖8
4.3.3:事件綁定
定義好了樣式,現在給“提交”按鈕綁定一個事件,現在改寫下FormTestSwing.initSouth方法:
public JPanel initSouth() {
//定義表單的提交按鈕,放置到IDEA會話框的底部位置
JButton submit = new JButton("提交");
submit.setHorizontalAlignment(SwingConstants.CENTER); //水平居中
submit.setVerticalAlignment(SwingConstants.CENTER); //垂直居中
south.add(submit);
//按鈕事件綁定
submit.addActionListener(e -> {
//獲取到name和age
String name = nameContent.getText();
String age = ageContent.getText();
//刷新r2標簽里的內容,替換為name和age
r2.setText(String.format("name:%s, age:%s", name, age));
});
return south;
}
現在再來點擊下“提交”按鈕,就可以輸出表單內容了:
圖9
4.4:插件綁定類:FormAction
之前講過,這個類是插件的入口,結合上面定義好的表單Dialog,來看下它是怎么寫的:
public class FromAction extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
FormTestDialog formTestDialog = new FormTestDialog(e.getProject());
formTestDialog.setResizable(true); //是否允許用戶通過拖拽的方式擴大或縮小你的表單框,我這里定義為true,表示允許
formTestDialog.show();
}
}
五、插件的打包&安裝
截止到第四步,都只是在調試IDE里查看效果,如果一個插件開發完成后,需要被實際的IDEA安裝,這個時候就需要借助打包選項來打包你的插件,點擊下面的選項構建插件:
圖10
構建完成后,查看build包下的distributions目錄,里面的zip包就可以直接安裝進你的IDEA:
圖11
然后選擇IDEA的Preferences下的plugins選項,彈出如下框,按照圖里的指示選擇zip包安裝即可:
圖12
然后安裝完成,重啟IDEA即可:
圖13
各個展示模塊對應插件項目里配置的來源參考下圖:
圖14
重啟后出現了跟調試IDEA里一樣的菜單欄,選中后運行成功:
圖15
寫在最后
截止到這里,一個插件的開發、調試、安裝就完成了,理論上通過這個簡單的例子就可以實現一些實際的功能了,因為其完整展示了數據輸入到數據獲取整個過程。
因為工作當中需要寫一個代碼生成器,想要以一個IDEA插件的方式提供服務,所以在這里做個記錄,防止以后再次用到時從零開始。。
要有一定的swing基礎,我在開發代碼生成器的時候,就是因為swing基礎太差,布局花了非常多的時間。
🍒 之后不會深入去研究插件的開發,如果后續工作中有用到插件開發的其他的功能點,會更新在這個系列里,如果想深入搞IDEA插件開發,建議看IDEA的官方文檔,官方文檔有點亂,有很多只是簡單介紹幾句甚至沒有示例,好在他們有個問答社區,建議搜索時用google搜英文關鍵詞(google對英文搜索支持強大,沒試過度娘,應該也可以搜到),里面會有人提問,比如版本兼容的問題就是google出來的,社區里正好有一篇問答。(鏈接放到開頭里了)