这次idea插件开发需求中主要是对动作、service、数据存储以及界面几方面的处理,这里做一些idea插件开发记录。
1、action
这里的action是指idea的操作动作,以下是创建动作。
这里常用的按键组件有ProjectViewPopupMenu(项目右键弹出菜单),ToolsMenu(idea工具菜单),EditorPopupMenu(编辑弹出菜单)。
在创建完成后,会在plugin.xml中注册,后面的first是该按钮在组件中的位置。
<actions>
<action id="GenerateOperationAction" class="com.xx.xx.xx.action.GenerateAction"
text="测试action">
<add-to-group group-id="ProjectViewPopupMenu" anchor="first"/>
</action>
</actions>
在生成action类后需要对其进行重写。
主要重写的方法有两个,一个是actionPerformed,另一个是update。actionPerformed是该按钮点击后的事件,一般弹出界面或者做相应业务逻辑。update是判断该按钮是否可以点击,如果不需要就直接调用父类方法即可。
@Override
public void actionPerformed(AnActionEvent e) {
//这里是弹出对话框
GenerateDialog dialog = new GenerateDialog();
dialog.setSize(1024, 500);
WindowUtils.displayToCenter(dialog);
}
@Override
public void update(@NotNull AnActionEvent event) {
boolean flag = false;
//这里是判断鼠标右键时是否在PROJECT_JAVA_PATH路径下方
VirtualFile file = CommonDataKeys.VIRTUAL_FILE.getData(event.getDataContext());
if (file != null && file.getPath().contains(Constants.Path.PROJECT_JAVA_PATH)) {
flag = true;
}
event.getPresentation().setEnabled(flag);
}
2、Service
在idea插件使用中需要对用户输入进行存储,用户数据的存储则存放在Service中。这里类似spring的@Service,在同一级别中保持单例。主要分为application、project、model三个级别。
在plugin.xlm中配置service
<extensions defaultExtensionNs="com.intellij">
<!-- 应用级别 -->
<applicationService serviceImplementation="com.xx.xx.xx.DBConfiguration"/>
</extensions>
一般在使用时会写一个静态方法去获取对应的对象,然后进行数据的写入或者获取
//数据存储的名称及位置
@State(name = "DBConfiguration", storages = {@Storage("DBConfiguration.xml")})
public class DBConfiguration implements PersistentStateComponent<DBConfiguration> {
//需要存储的用户数据
public String xxxx;
//对于service对象的获取
public static DBConfiguration getInstance() {
DBConfiguration dbConfiguration = ServiceManager.getService(DBConfiguration.class);
return dbConfiguration;
}
@Nullable
@Override
public DBConfiguration getState() {
return this;
}
//持久化的xml中获取数据到对象
@Override
public void loadState(@NotNull DBConfiguration DBConfiguration) {
XmlSerializerUtil.copyBean(DBConfiguration, this);
}
}
3、界面
界面可以手写java的gui或则idea提供的拖动组件,这里我用的是拖动组件。
在创建完后会生成一个java类和一个form文件,java类中可以写构造函数以及相应的业务逻辑,点击form文件可以进行组建拖动。
比如拖动组件生成了以下界面
java文件
public class PluginSettingForm {
private JPanel mainPanel;
private JTextField dbHostField;
private JTextField accountField;
private JPasswordField pwdField;
private JButton testButton;
public PluginSettingForm() {
//对相应位置field进行设置
dbHostField.setToolTipText("这里是tip");
dbHostField.setText("这里是text");
testButton.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
//这里是鼠标动作,业务逻辑
super.mouseClicked(e);
}
});
}
}
界面主要是绘制自定义界面比较麻烦,比如带有复选框的JList,带复选框的JTree,这个略过。
4、idea的setting中tools设置对应的用户数据
一个插件一般会有一个插件数据设置的界面,在这个界面中设置插件所需要的相关数据,比如连接数据库,则需要上图中的数据库地址、用户、密码等数据。这里需要写一个界面即上图所示,并且注册到相应的位置。
plugin.xml
<extensions defaultExtensionNs="com.intellij">
<projectConfigurable parentId="tools" id="xx.xx.Configuration" displayName="GeneratePlugin"
nonDefaultProject="true"
instance="xx.xx.xx.xx.Configurable"/>
</extensions>
注册完后需要重写方法
public class Configurable implements SearchableConfigurable {
private PluginSettingForm mainForm;
private DBConfiguration dBConfiguration;
@Override
public @NotNull String getId() {
return "xx.xx.Configuration";
}
//展示名称
@Override
public @Nls(capitalization = Nls.Capitalization.Title) String getDisplayName() {
return "Generate Plugin";
}
//创建界面
@Override
public @Nullable JComponent createComponent() {
dBConfiguration = DBConfiguration.getInstance();
return getForm().getMainPanel();
}
@Override
public boolean isModified() {
return true;
}
//设置数据
@Override
public void apply() {
getForm().apply(dBConfiguration);
dBConfiguration.fireConfigChanged();
}
@NotNull
private PluginSettingForm getForm() {
if (mainForm == null) {
mainForm = new PluginSettingForm();
}
return mainForm;
}
//重置数据
@Override
public void reset() {
getForm().reset(dBConfiguration);
}
}
展示图如下:
5、项目引导
点击 File >> New >> Project
这里需要自定义一个ModuleBuilder继承ModuleBuilder
public class MyProjectModuleBuilder extends ModuleBuilder {
//Module的类型,这里用JAVA
@Override
public ModuleType<?> getModuleType() {
return StdModuleTypes.JAVA;
}
//这里设置图标
@Override
public Icon getNodeIcon() {
return ICON;
}
@Override
public @Nullable String getBuilderId() {
return "xxxxId";
}
//名称
@Override
public String getPresentableName() {
return "MyProject";
}
//位置
@Override
public String getParentGroup() {
return "Build Tools";
}
//第一个页面,即红色框的界面
@Override
public @Nullable ModuleWizardStep getCustomOptionsStep(WizardContext context, Disposable parentDisposable) {
//自定义个一个向导步骤,内部有两个方法,一个是获得界面,一个是下一步的逻辑操作
return new MyProjectModuleConfigStep(new MyProjectUI());
}
//第2-n步骤的界面
@Override
public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
List<ModuleWizardStep> list = new ArrayList<>();
//这里是判断用户是否有填数据,没有就加一个填写用户数据的界面
DBConfiguration instance = DBConfiguration.getInstance();
if (instance == null || instance.getConfigInfo() == null) {
list.add(new MyProjectModuleDbConfigdStep(new PluginSettingForm()));
}
//这里是加后续的步骤界面,可以加n多个
list.add(new MyProjectModuleSecondStep(new MyProjectFinishUI()));
ModuleWizardStep[] steps = new ModuleWizardStep[list.size()];
list.toArray(steps);
return steps;
}
//设置模块名
@Override
public @Nullable ModuleWizardStep modifySettingsStep(@NotNull SettingsStep settingsStep) {
ModuleNameLocationSettings moduleNameLocationSettings = settingsStep.getModuleNameLocationSettings();
String artifactId = MyProjectConfiguration.getInstance().getArtifact();
if (null != moduleNameLocationSettings && !StringUtil.isEmptyOrSpaces(artifactId)) {
moduleNameLocationSettings.setModuleName(artifactId);
}
return super.modifySettingsStep(settingsStep);
}
@Override
public void cleanup() {
super.cleanup();
}
//module创建完成回调函数
@Override
public void setupRootModel(@NotNull ModifiableRootModel rootModel) throws ConfigurationException {
// 设置 JDK
if (null != this.myJdk) {
rootModel.setSdk(this.myJdk);
} else {
rootModel.inheritSdk();
}
// 生成工程路径
String path = FileUtil.toSystemIndependentName(Objects.requireNonNull(getContentEntryPath()));
new File(path).mkdirs();
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
rootModel.addContentEntry(virtualFile);
Project project = rootModel.getProject();
}
}
自定义引导页步骤ModuleWizardStep
public class MyProjectModuleSecondStep extends ModuleWizardStep {
//界面
private MyProjectFinishUI myProjectFinishUI;
public MyProjectModuleSecondStep(MyProjectFinishUI myProjectFinishUI) {
this.myProjectFinishUI = myProjectFinishUI;
}
//获取界面
@Override
public JComponent getComponent() {
return myProjectFinishUI.getComponent();
}
//点击next后所做的业务逻辑,一般是存储用户数据
@Override
public void updateDataModel() {
MyProjectConfiguration instance = MyProjectConfiguration.getInstance();
String appName = myProjectFinishUI.getAppName();
instance.setAoneName(aoneName);
}
}