文章目录[x]
- 1:一、开发工具
- 2:二、plugin.xml介绍
- 3:三、action介绍
- 4:四、IOC注解生成器插件
- 4.1:获取光标所在行的layout文件
- 4.2:获取整个工程的layout文件,然后遍历id
- 4.3:生成Dialog供用户选择id生成代码
- 4.4:生成IOC注解代码
- 5:五、生成jar包供Android Studio使用
在系列Android架构中,将持续更新项目中用到的架构和技术,该项目的已发布至github。文中的插件代码位于MelroseIOC仓库下(插件适用于Java代码);
一、开发工具
该插件通过Intelli IDEA开发,创建项目的具体步骤如下:
1、Create new Project;
2、选择IntelliJ Platform Plugin;
3、输入项目名创建;
在项目创建完成后会生成如下目录的项目文件:
- resources
- META-INF
- plugin.xml
- src
其中resources中可以存放资源文件,plugin.xml中可以配置插件的动作等。
二、plugin.xml介绍
在plugin.xml文件中,主要有如下属性标签:
- id:插件的id
- name:插件的名字
- version:插件的版本
- actions:插件中执行的动作组
- vendor:开发者信息
- description:对该插件的描述
三、action介绍
在src目录下右键new中的Plugin DevKit中创建Action,创建成功后会在plugin.xml中生成插件标签;
- groups:代表菜单显示的地方,CodeMenu(Code)表示显示在Code菜单栏中;
- Anchor:代表显示的位置 first last
- Keyboard Shortcuts:设置快捷键
四、IOC注解生成器插件
在项目MelroseIOC中,有该插件的具体的实现逻辑。下面将介绍主要的逻辑代码:
获取光标所在行的layout文件
显示获取光标所在的行数,然后获取该行的字符串,判断是否存在R.layout资源文件,如果有则截取获取该资源文件文件名,然后返回该文件名;
/**
* 获取当前光标的layout文件 ;
* @param editor 当前光标 ;
* @return 返回文件名 ;
*/
private String getCurrentLayout(Editor editor){
Document document = editor.getDocument();
CaretModel caretModel =editor.getCaretModel();
int caretOffset = caretModel.getOffset();
int lineNum = document.getLineNumber(caretOffset);
int lineStartOffset = document.getLineStartOffset(lineNum);
int lineEndOffset = document.getLineEndOffset(lineNum);
//获取光标所在的行的数据
String lineContent =document.getText(new TextRange(lineStartOffset,
lineEndOffset));
String layoutMatching = "R.layout";
if (!TextUtils.isEmpty(lineContent)&& lineContent.contains(layoutMatching)){
//获取layout文件的字符串
int startPosition = lineContent.indexOf(layoutMatching)+layoutMatching.length();
int endPosition = lineContent.indexOf(")",startPosition);
String layoutStr = lineContent.substring(startPosition,endPosition);
//可能是另外一种情况 View.inflate
if (layoutStr.contains(",")){
endPosition = lineContent.indexOf(",",startPosition);
layoutStr = lineContent.substring(startPosition,endPosition);
}
return layoutStr;
}
return null;
}
获取整个工程的layout文件,然后遍历id
在上一步获取到资源文件的文件名后,然后通过下面的方法,来通过解析xml找到所以的id生成Element;
/**
* 获取所有id
*
* @param file layout文件 ;
* @param elements 元素
* @return
*/
public static java.util.List<Element> getIDsFromLayout(final PsiFile file, final java.util.List<Element> elements) {
// To iterate over the elements in a file
// 遍历一个文件的所有元素
file.accept(new XmlRecursiveElementVisitor() {
@Override
public void visitElement(PsiElement element) {
super.visitElement(element);
// 解析Xml标签
if (element instanceof XmlTag) {
XmlTag tag = (XmlTag) element;
// 获取Tag的名字(TextView)或者自定义
String name = tag.getName();
// 如果有include
if (name.equalsIgnoreCase("include")) {
// 获取布局
XmlAttribute layout = tag.getAttribute("layout", null);
// 获取project
Project project = file.getProject();
// 布局文件
XmlFile include = null;
PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, getLayoutName(layout.getValue()) + ".xml", GlobalSearchScope.allScope(project));
if (psiFiles.length > 0) {
include = (XmlFile) psiFiles[0];
}
if (include != null) {
// 递归
getIDsFromLayout(include, elements);
return;
}
}
// 获取id字段属性
XmlAttribute id = tag.getAttribute("android:id", null);
if (id == null) {
return;
}
// 获取id的值
String idValue = id.getValue();
if (idValue == null) {
return;
}
XmlAttribute aClass = tag.getAttribute("class", null);
if (aClass != null) {
name = aClass.getValue();
}
// 添加到list
try {
Element e = new Element(name, idValue, tag);
elements.add(e);
} catch (IllegalArgumentException e) {
}
}
}
});
return elements;
}
生成Dialog供用户选择id生成代码
在上一步获取到资源文件所有的ID生成的Element后,通过swing GUI即可生成弹窗;
/**
* 解析mElements,并添加到JPanel
*/
private void initContentPanel() {
mContentJPanel.removeAll();
// 设置内容
for (int i = 0; i < mElements.size(); i++) {
Element mElement = mElements.get(i);
IdBean itemJPanel = new IdBean(new GridLayout(1, 4, 10, 10),
new EmptyBorder(5, 10, 5, 10),
new JCheckBox(mElement.getName()),
new JLabel(mElement.getId()),
new JCheckBox(),
new JTextField(mElement.getFieldName()),
mElement);
// 监听
itemJPanel.setEnableActionListener(this);
itemJPanel.setClickActionListener(clickCheckBox -> mElement.setIsCreateClickMethod(clickCheckBox.isSelected()));
itemJPanel.setFieldFocusListener(fieldJTextField -> mElement.setFieldName(fieldJTextField.getText()));
mContentJPanel.add(itemJPanel);
mContentConstraints.fill = GridBagConstraints.HORIZONTAL;
mContentConstraints.gridwidth = 0;
mContentConstraints.gridx = 0;
mContentConstraints.gridy = i;
mContentConstraints.weightx = 1;
mContentLayout.setConstraints(itemJPanel, mContentConstraints);
}
mContentJPanel.setLayout(mContentLayout);
jScrollPane = new JBScrollPane(mContentJPanel);
jScrollPane.revalidate();
// 添加到JFrame
getContentPane().add(jScrollPane, 1);
}
生成IOC注解代码
在用户选完要生成ID注解之后,即可通过如下几个方法来生成注解代码;
/**
* 创建一个点击事件
*/
private void createClickMethod(String methodName, Element element) {
// 拼接方法的字符串
StringBuilder methodBuilder = new StringBuilder();
methodBuilder.append("@OnClick(" + element.getFullID() + ")\n");
methodBuilder.append("private void " + methodName + "(" + element.getName() + " " + getClickMethodName(element) + "){");
methodBuilder.append("\n}");
// 创建OnClick方法
mClass.add(mFactory.createMethodFromText(methodBuilder.toString(), mClass));
}
/**
* 创建变量
*/
private void generateFields() {
for (Element element : mElements) {
if (mClass.getText().contains("@ViewById(" + element.getFullID() + ")")) {
// 不创建新的变量
continue;
}
// 设置变量名,获取text里面的内容
String text = element.getXml().getAttributeValue("android:text");
if (TextUtils.isEmpty(text)) {
// 如果是text为空,则获取hint里面的内容
text = element.getXml().getAttributeValue("android:hint");
}
// 如果是@string/app_name类似
if (!TextUtils.isEmpty(text) && text.contains("@string/")) {
text = text.replace("@string/", "");
// 获取strings.xml
PsiFile[] psiFiles = FilenameIndex.getFilesByName(mProject, "strings.xml", GlobalSearchScope.allScope(mProject));
if (psiFiles.length > 0) {
for (PsiFile psiFile : psiFiles) {
// 获取src\main\res\values下面的strings.xml文件
String dirName = psiFile.getParent().toString();
if (dirName.contains("src\\main\\res\\values")) {
text = Util.getTextFromStringsXml(psiFile, text);
}
}
}
}
StringBuilder fromText = new StringBuilder();
if (!TextUtils.isEmpty(text)) {
fromText.append("/****" + text + "****/\n");
}
fromText.append("@ViewById(" + element.getFullID() + ")\n");
fromText.append("private ");
fromText.append(element.getName());
fromText.append(" ");
fromText.append(element.getFieldName());
fromText.append(";");
// 创建点击方法
if (element.isCreateFiled()) {
// 添加到class
mClass.add(mFactory.createFieldFromText(fromText.toString(), mClass));
}
}
}
五、生成jar包供Android Studio使用
通过点击build菜单栏下的Prepare Plugin Module ‘MelroseIOC’ For Deployment即可生成jar包;