目标
输入
- 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
的源码.