由于工作需要以及个人兴趣, 最近学习了自定义AndroidStudio插件用于生成代码, 在此记录下以备以后查阅, 希望同时也能帮助到正在看这篇博客的你.
编写IDEA(AndroidStudio)插件需要用到 IntelliJIDEA , 编辑器可以在官网(官网需要访问)下载, 激活请访问这个地址, (修改hosts然后粘贴激活码)
#请将hosts添加以下值
0.0.0.0 account.jetbrains.com
0.0.0.0 www.jetbrains.com
进入IDEA编辑器, 选择新建工程, 如图所示, 选中 IntelliJ Platform Plugin, Module SDK 的位置如果没有需要点击 New 按钮, 默认会选中 IDEA 所在目录, 直接点确定即可.
打开工程后可以看到如下图的目录结构, 其中plugin.xml就是插件配置的地方
按照以下注释配置
<idea-plugin version="2">
<id>com.your.company.unique.plugin.idid>
<name>Plugin display name herename>
<version>1.0version>
<vendor email="[email protected]" url="http://www.yourcompany.com">YourCompanyvendor>
<description>
most HTML tags may be used
]]>description>
<change-notes>
most HTML tags may be used
]]>
change-notes>
<idea-version since-build="145.0"/>
<extensions defaultExtensionNs="com.intellij">
extensions>
<actions>
actions>
idea-plugin>
接下来在 src 中创建一个Action
如下图, ActionID 之后可以在 plugins.xml中修改, className就是类名, Name是显示给用户的动作名称
Groups 是指你创建的 Action 出现的位置, 这里我选择了GenerateGroup, 对应如下图 Generate 组, 配置完点击OK, Keyboard Shortcuts可以选择不填
当我们创建一个 Action 后将生成以下代码, 我们的逻辑都要写到actionPerformed
方法中.
public class DemoPlugins extends AnAction {
@Override
public void actionPerformed(AnActionEvent e) {
// TODO: insert action logic here
}
}
AnActionEvent 是actionPerformed方法暴露出来的唯一一个参数, 是插件开发的桥梁, 通过 AnActionEvent 可以获取到很多东西, 对此我了解的也不多, 大家可以通过查看注释和官方文档来了解AnActionEvent。
据我所知,AnActionEvent有一个 event.getData(DataKeys.EDITOR)
的方法能获取到很多东西, 比如传入 DataKeys.EDITOR
能获取到一个光标对象。比如如下代码可以通过光标类获取到光标所在的类, 返回的类型是PsiClass
, PSI是IDEA插件开发文件操作的核心, 稍后详细介绍.
/**
* 获取光标所在的类
*/
public static PsiClass getCurrentClass(AnActionEvent event){
Editor editor = event.getData(DataKeys.EDITOR);
PsiElement element = getCurrentFile(event).findElementAt(editor != null ? editor.getCaretModel().getOffset() : 0);
return PsiTreeUtil.getParentOfType(element, PsiClass.class);
}
Dialog: 在src目录下, 右键 – > New – > Dialog , 创建DIalog。
将生成两个文件:XXXDialog.java
和 XXXDialog.form
, 其中form是图形化界面, 可以拖拖拽拽来摆放控件, 而java文件用于处理控件的各种事件, 类似Android中的 layout布局和Activity的关系。这其中的所有控件都是 java swing 中的控件, 至于控件的用法可以直接百度具体控件(比如百度JTable), 网上有很多非常详细的教程。
使用如下代码将Dialog显示于界面上:
XXXDialog dialog = new XXXDialog();
dialog.setSize(500, 500);//设置Dialog大小
dialog.setLocationRelativeTo(null);//使Dialog展示在屏幕中央
dialog.setVisible(true);//使Dialog可见, 调用此方法后Dialog才会展示在屏幕上
IDEA SDK提供PropertiesComponent
来持久化数据, 通过PropertiesComponent.getInstance().setValue(key, value);
保存, PropertiesComponent.getInstance().getValue(key);
获取, 建议配合Gson使用:
/**
* 保存配置至磁盘
*/
public static <T> void save(@NotNull T config){
PropertiesComponent.getInstance().setValue(config.getClass().getName(), new Gson().toJson(config));
}
/**
* 读取配置
*/
public static @NotNull <T> T load(@NotNull Class<T> clazz, T defaultValue){
String json = PropertiesComponent.getInstance().getValue(clazz.getName());
if(json == null){
return defaultValue;
}
return new Gson().fromJson(json, clazz);
}
关于如何添加三方jar文件, 需要点击 Project Structure(如上图)图标, 选择Modules – > Dependencies – > 点击加号添加(如下图), 搜索 gson点击OK即可, 当然也可以添加Jar文件。
//A.java文件中
public class A{
static class B{
|//现在光标在此处
} //B是一个内部类
}
/**
* 获取光标所在的类
*/
public static PsiClass getCurrentClass(AnActionEvent event){
Editor editor = event.getData(DataKeys.EDITOR);
PsiElement element = getCurrentFile(event).findElementAt(editor != null ? editor.getCaretModel().getOffset() : 0);
return PsiTreeUtil.getParentOfType(element, PsiClass.class);
}
------
{
PsiClass B = getCurrentClass(event); //B的PsiClass对象
PsiClass A = ((PsiClass)B.getParent());//得到A的PsiClass对象
PsiFile AFile = ((PsiFile)A.getParent());//得到A的文件对象
//也就是说可以通过内部类获取到外部类
}
interface
class
enum
都是一个PsiClass对象可以创建注释 方法 类 接口 注解 变量等, 通过JavaPsiFacade.getElementFactory(project);
来获取该对象, project 可以通过 AnActionEvent 或任意一个 PsiElement 来获取
注意: PsiElementFactory.createXXXFromText(“xxx”, PsiElement) 类似这样的方法, 第一个参数不能包含’\t’制表符
使用 PsiElementFactory 来创建一个常量:
public static PsiElementFactory getElementFactory(PsiElement element){
return JavaPsiFacade.getElementFactory(element.getProject());
}
/**
* 创建带注释的String常量
* @param constName 变量名
* @param constValue 变量的值
* @param javaDoc 变量的注释
*/
public static void createPrivateStringConstFailed(PsiClass psiClass, String constName, String constValue, String javaDoc){
PsiType psiType = PsiType.getTypeByName("java.lang.String", psiClass.getProject()
, GlobalSearchScope.EMPTY_SCOPE);//创建一个数据类型
// 根据变量名查找类种是否包含此变量, 第二个参数含义是是否查找父类
PsiField psiField = psiClass.findFieldByName(constName, false);
if(psiField == null) {
//避免生成重复变量
//使用PsiElementFactory创建一个成员变量
psiField = getElementFactory(psiClass).createField(constName, psiType);
//PsiUtil是SDK提供的一个工具类
//给成员变量添加修饰符, 参数2为true代表添加, false代表移除
PsiUtil.setModifierProperty(psiField, PsiModifier.STATIC, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.FINAL, true);
PsiUtil.setModifierProperty(psiField, PsiModifier.PRIVATE, true);
//设置变量初始化表达式
PsiExpression psiInitializer = getElementFactory(psiClass).createExpressionFromText("\"" + constValue + "\"", psiField);
psiField.setInitializer(psiInitializer);
//在变量后写入单行注释
writeComment(psiClass, javaDoc, psiField);
//将变量添加至类中
psiClass.add(psiField);
}else {
PsiExpression psiInitializer = getElementFactory(psiClass).createExpressionFromText("\"" + constValue + "\"", psiField);
psiField.setInitializer(psiInitializer);
writeComment(psiClass, javaDoc, psiField);
}
}
private static void writeComment(PsiClass psiClass, String javaDoc, PsiField psiField) {
if(javaDoc != null) {
//获取变量最后一个元素
PsiElement lastChild = psiField.getLastChild();
if(!(lastChild instanceof PsiComment)) {
//避免重复写入注释
//创建一个单行注释
PsiComment comment = PsiUtils.getElementFactory(psiClass).createCommentFromText("//" + javaDoc, psiField);
//将单行注释添加至最后一个元素后
psiField.addAfter(comment, lastChild);
}
}
}
创建一个接口:
/**
* 创建一个接口
*/
public static PsiClass createInterface(PsiClass clazz, String interfaceCame){
PsiClass innerInterface = getElementFactory(clazz).createInterface(interfaceCame);
PsiModifierList modifierList = innerInterface.getModifierList();
modifierList.setModifierProperty(PsiModifier.PUBLIC, true);
return innerInterface;
}
为变量 方法 或接口 类添加注解: PsiElement.getModifierList().addAnnotation("NotNull()");
不需要加@
符号
创建一个方法:
PsiClass clazz;//目标类
StringBuilder sb = new StringBuilder();
sb.append("/**\n");
sb.append(" * ").append("注释内容").append("\n");
sb.append("* @param value woshicnahsumiaoshu").append("\n");
sb.append(" */\n");
sb.append("@Click(").append(id).append(")\n");
sb.append("void onClick(View view) {\n\n}");
String text = sb.toString();
JavaPsiFacade.getElementFactory(clazz.getProject()).createMethodFromText(text, clazz);
本来想着重写Psi 文件操作相关的东西的, 结果写的时候发下无从下笔, 还是对IDEA插件开发了解的不够多, 写的有点乱, Psi相关大家可以通过关联源码后看注释来了解每个函数的作用, 一般情况下使用插件生成代码都可以使用PsiElementFactory
来创建, 使用PsiElement.add(PsiElement)
来添加, 感兴趣的可以通过写Demo尝试