Android架构(二)- IOC注解框架插件开发

文章目录[x]
  1. 1:一、开发工具
  2. 2:二、plugin.xml介绍
  3. 3:三、action介绍
  4. 4:四、IOC注解生成器插件
  5. 4.1:获取光标所在行的layout文件
  6. 4.2:获取整个工程的layout文件,然后遍历id
  7. 4.3:生成Dialog供用户选择id生成代码
  8. 4.4:生成IOC注解代码
  9. 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包;

 

点赞

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00