目标
输入
- url地址, 可能包含一级域名和二级域名
- get/post参数, 键值对形式, 可以动态增减
- 接口返回的json结构
输出
- 继承特定类的请求类的java文件
- 根据url实现相应的方法
- 根据get/post参数创建内部类, 并且实现
toString方法, 并且创建对应的构建函数 - 根据json结构创建实体类, 并实现
toString方法
实现
涉及的行为
- 从界面获取相关值
- 使用列表表示参数, 所以需要动态增加/删除控件
- 获取目录路径, 并创建java文件
- 往java文件写入类定义, 变量定义和方法定义
- 解析json
实现界面
一个界面一般包含两个文件, form file和UI class.
form file
类似Android里面的xml布局文件, 用来描述界面的布局. 使用GUI Designer(类似Android Studio的布局预览器)通过拖动控件来创建静态布局.
控件分为component和container,关系和Android中的View和ViewGroup差不多, 其中container会要求指定部分仅对container生效的属性.
一般
container使用默认的GridLayoutManager (IntelliJ)就可以满足要求了.
大小
-
Horizontal/Vertical Size Policy: 决定了当该component所属的container大小发生变化时component的尺寸变化规则. 该属性只有GridLayoutManager具有, 其中可以赋值下面三个值
a.canShrink: 实际值可以比指定值(preferred size)小.
b.canGrow: 实际值可以比指定值大.
c.wantGrow: 实际值会比指定值大. 优先级高于canGrow, 类似match_parent, 会填充container -
Preferred Size: 二维数组指定宽高, 对于GridLayoutManager可以单独指定宽或者高,-1表示该值由动态计算得出.
位置
不同的LayoutManager有不同的位置属性.
对于GridLayoutManager来说, 整个布局就是一个网格, 通过网格控制component的位置, 跟Excel有点类似.
对于对齐, 是通过
HSpacer/VSpacer来占据空间来对齐的.
使用GUI Designer进行布局的时候, 如果是GridLayoutManager, 插入时会有提示插入的行号和列号, 注意网格的嵌套关系就能轻松把控件放在目标位置了, 另外通过拉伸控件可以合并网格.
UI class
布局实际使用的是Swing, 因此在UI class中涉及控件的操作都可以查阅
Swing的官方教程.
关于动态增删控件, 在Swing中, 在
JLIst中的按钮是不会响应点击功能的, 不熟悉Swing浪费了很多时间...
动态增加控件
因此不能用JLIst, JTable过于复杂, 因此直接往JPanel中添加控件然后重绘来实现动态增加控件的效果.
具体的代码参考官方教程, 只记录遇到的几个坑:
注意: 下面的说法没有深究, 有可能是错误的.
布局
使用GridLayoutManager不能动态增加网格, 在这里要实现类似列表的效果, 对吼使用的是BoxLayout.
对齐
使用BoxLayout后, 增加控件会自动居中, 需要通过Box.createVerticalGlue()来创建占位控件来把增加的控件顶至顶部实现顶部对齐的效果.
重绘
增加或者删除控件之后, 需要调用revalidate()和repaint()方法来重绘控件才能正常显示.
获取输入值
通过控件获取输入值非常简单, 类似Android, 控件具有各种getXXXX方法来获取输入值.
获取目录, 创建文件
PSI
插件开发中, 使用PSI Files来表示具体操作的文件.
PSI(Program Structure Interface) file是一个接口, 使用树状结构表示文件中的内容.
对于特定的文件一般有相应的子类, 例如PsiJavaFile表示一个Java文件.
如何获取PSI
- 在Action中通过
AnActionEvent#getData(LangDataKeys.PSI_FILE) - 在VirtualFile中通过
PsiManager.getInstance(project).findFile() - 在Document中通过
PsiDocumentManager.getInstance(project).getPsiFile() - 通过
psiElement.getContainingFile()从element实例获取 - 在project中的任意位置通过文件名获取
FilenameIndex.getFilesByName(project, name, scope)
这个插件的入口是Action, 所以可以通过Action就能获取到当前用户所在的Java文件的PSI实例.
PSI的操作可以参考PSI Cookbook.
获取目录
当已经有PsiFile实例时, 可以通过获取PsiFile#getParent()获取到目录实例PsiDirectory.
创建类文件
对于Java来说, 可以通过JavaDirectoryService#createClass(dir, fileName)来创建类文件, 同时会返回一个PsiClass实例表示该类.
修改类文件
对于Java, 修改类文件首先要获取想要修改的对象对应的PsiElement子类实例(下面称PsiElement为元素), 例如类定义对应PsiClass实例, 方法定义对应PsiMethod等等.
在Java文件中所有东西都是用元素表示的. 整个Java文件是一棵元素树, 根节点是一个
PsiClass.
通过PsiElement#add()可以给这个元素添加其他元素.
删除对象包含的元素则需要调用要删除的元素的PsiElement#delete()方法.
大部分
PsiElement都可以通过PsiElementFactory中的相关方法获取.
修改修饰符
只要部分元素有修饰符, 这部分元素都会继承
PsiModifierListOwner.
通过PsiModifierListOwner#getModifierList()可以获取修饰符列表PsiModifierList实例.
注意修饰符列表本身也是一个元素.
通过PsiElement#add()方法就可以增加修饰符.
通过遍历PsiModifierList中包含的元素, 然后调用包含的元素的PsiElement#delete()来删除修饰符.
修改继承关系
只有类元素有继承关系.
与修饰符类似, 通过PsiClass#getExtendsList()来获取继承关系列表, 然后通过add操作来增加继承关系.
删除继承关系需要PsiClass#getExtendsList().getReferenceElements()来获取元素, 然后删除这些元素.
写入变量
变量元素对应的实例为PsiField, 可以通过PsiElementFactory#createField来创建变量, 创建变量时需要通过PsiType指定变量的类型, 同样可以通过PsiElementFactory#create
写入方法
方法元素对应的实例为PsiMethod.
构造函数属于方法元素, 通过PsiElementFactory#createConstructor()来创建.
方法变量
只有方法元素有方法变量.
可以通过PsiMethod#getParameterList()获取方法变量列表, 然后修改.
方法变量也是一个元素, 实例是PsiParameter, 同样可以通过工厂方法创建, 创建时需要指定变量类型.
方法体
方法体也是一个元素, 实例是PsiCodeBlock
通过PsiMethod#getBody()来获取方法体元素, 然后通过增加statement和expression来写方法体内容.
理所当然, statement和expression也是元素, 可以通过工厂方法创建.
解析json
解析json需要引入json库, 右键项目有Open Module Settings选项, 打开面板有Dependencies面板, 可以添加依赖.
后续就是简单的创建类和变量了. 不再累述.
总结
- 关键是取得
PsiFile类实例, 一切文件操作都是以此为根据. - 理解
PsiElement元素, 整个文件就是一棵元素树. 类是元素, 方法是元素, 方法体是元素, 语句是元素, 关键字也是元素. -
PsiElementFactory是个好东西, 能够很方便地创建元素, 其中的createXXXFromText更是个黑科技. - 对于Java来说,
JavaPsiFacade也是个好东西. 能够通过名称获取PsiClass等 - 读写文件需要使用
WriteCommandAction.runWriteCommandAction()来启用工作线程. - 官方文档很水, 论坛还凑合.
- 方法的使用可以看源码的注释. 例如
PsiElementFactory的源码.