├── .idea ├── .name ├── vcs.xml ├── modules.xml ├── misc.xml └── uiDesigner.xml ├── test.json ├── DarrenTranslate.jar ├── resources └── META-INF │ ├── 插件使用演示.gif │ ├── 插件安装演示.gif │ └── plugin.xml ├── README.md ├── DarrenTranslate.iml └── src ├── entity ├── Element.java └── IdBean.java ├── action └── DarrenIOC.java ├── Utils ├── ViewFieldMethodCreator.java └── Util.java └── View └── FindViewByIdDialog.java /.idea/.name: -------------------------------------------------------------------------------- 1 | DarrenIOC -------------------------------------------------------------------------------- /test.json: -------------------------------------------------------------------------------- 1 | { 2 | "userName": "Darren" 3 | } 4 | -------------------------------------------------------------------------------- /DarrenTranslate.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HCDarren/DarrenIOC/HEAD/DarrenTranslate.jar -------------------------------------------------------------------------------- /resources/META-INF/插件使用演示.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HCDarren/DarrenIOC/HEAD/resources/META-INF/插件使用演示.gif -------------------------------------------------------------------------------- /resources/META-INF/插件安装演示.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HCDarren/DarrenIOC/HEAD/resources/META-INF/插件安装演示.gif -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DarrenIOC 2 | The world of mortals beautiful code 3 | 4 | Android Studio Darren注解IOC框架插件 5 | 6 | 简介:这是本人写的一个插件,主要用于自己的IOC注解框架中,需要配合本人的开发框架使用。有点类似于ButterKnife注解框架,自己也是仿照网上写,大家 7 | 可以下载源码里面有大量的注释。 8 | 9 | 使用:如果你正在使用我的框架,那么只需要下载jar包插件安装即可,如果没有使用本人的框架就没什么价值所以也就没有发布插件,只是自己和同伴用用而已 10 | 11 | 插件使用演示: 12 | ![效果演示](https://github.com/Shenmowen/DarrenIOC/blob/4409818164dc7f8d6c922c0f7188dd2f5817622d/resources/META-INF/插件使用演示.gif) 13 | 14 | -------------------------------------------------------------------------------- /DarrenTranslate.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.hc.darren.ioc 3 | DarrenIOC 4 | 1.0 5 | YourCompany 6 | 7 | 10 | 11 | 13 | 添加可选生成的字段,可编辑变量名,可选择是否LayoutInflater类型,
14 | 添加快捷键Ctrl+Alt+H,text、hint的值添加到字段注释。 15 | ]]> 16 |
17 | 18 | 19 | 20 | 21 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
-------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/entity/Element.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import Utils.Util; 4 | import com.intellij.psi.xml.XmlTag; 5 | import org.apache.http.util.TextUtils; 6 | 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | public class Element { 11 | 12 | // 判断id正则 13 | private static final Pattern sIdPattern = Pattern.compile("@\\+?(android:)?id/([^$]+)$", Pattern.CASE_INSENSITIVE); 14 | // id 15 | private String id; 16 | // 名字如TextView 17 | private String name; 18 | // 命名1 aa_bb_cc; 2 aaBbCc 3 mAaBbCc 19 | private int fieldNameType = 3; 20 | private String fieldName; 21 | private XmlTag xml; 22 | // 是否生成 23 | private boolean isCreateFiled = true; 24 | // 是否Clickable 25 | private boolean isCreateClickMethod = false; 26 | 27 | /** 28 | * 构造函数 29 | * 30 | * @param name View的名字 31 | * @param id android:id属性 32 | * @throws IllegalArgumentException When the arguments are invalid 33 | */ 34 | public Element(String name, String id, XmlTag xml) { 35 | // id 36 | final Matcher matcher = sIdPattern.matcher(id); 37 | if (matcher.find() && matcher.groupCount() > 1) { 38 | this.id = matcher.group(2); 39 | } 40 | 41 | if (this.id == null) { 42 | throw new IllegalArgumentException("Invalid format of view id"); 43 | } 44 | 45 | String[] packages = name.split("\\."); 46 | if (packages.length > 1) { 47 | // com.example.CustomView 48 | this.name = packages[packages.length - 1]; 49 | } else { 50 | this.name = name; 51 | } 52 | 53 | this.xml = xml; 54 | } 55 | 56 | public String getId() { 57 | return id; 58 | } 59 | 60 | public void setId(String id) { 61 | this.id = id; 62 | } 63 | 64 | public String getName() { 65 | return name; 66 | } 67 | 68 | public void setName(String name) { 69 | this.name = name; 70 | } 71 | 72 | public int getFieldNameType() { 73 | return fieldNameType; 74 | } 75 | 76 | public void setFieldNameType(int fieldNameType) { 77 | this.fieldNameType = fieldNameType; 78 | } 79 | 80 | public XmlTag getXml() { 81 | return xml; 82 | } 83 | 84 | public void setXml(XmlTag xml) { 85 | this.xml = xml; 86 | } 87 | 88 | // 是否创建Filed属性 89 | public void setIsCreateFiled(boolean isCreateFiled){ 90 | this.isCreateFiled = isCreateFiled; 91 | } 92 | 93 | public boolean isCreateFiled(){ 94 | return isCreateFiled; 95 | } 96 | 97 | // 是否创建Click方法 98 | public void setIsCreateClickMethod(boolean isCreateClickMethod){ 99 | this.isCreateClickMethod = isCreateClickMethod; 100 | } 101 | 102 | public boolean isCreateClickMethod(){ 103 | return isCreateClickMethod; 104 | } 105 | 106 | /** 107 | * 获取id,R.id.id 108 | * 109 | * @return 110 | */ 111 | public String getFullID() { 112 | StringBuilder fullID = new StringBuilder(); 113 | String rPrefix = "R.id."; 114 | fullID.append(rPrefix); 115 | fullID.append(id); 116 | return fullID.toString(); 117 | } 118 | 119 | /** 120 | * 获取变量名 121 | * 122 | * @return 123 | */ 124 | public String getFieldName() { 125 | if (TextUtils.isEmpty(this.fieldName)) { 126 | String fieldName = id; 127 | String[] names = id.split("_"); 128 | if (fieldNameType == 2) { 129 | // aaBbCc 130 | StringBuilder sb = new StringBuilder(); 131 | for (int i = 0; i < names.length; i++) { 132 | if (i == 0) { 133 | sb.append(names[i]); 134 | } else { 135 | sb.append(Util.firstToUpperCase(names[i])); 136 | } 137 | } 138 | fieldName = sb.toString(); 139 | } else if (fieldNameType == 3) { 140 | // mAaBbCc 141 | StringBuilder sb = new StringBuilder(); 142 | for (int i = 0; i < names.length; i++) { 143 | if (i == 0) { 144 | sb.append("m"); 145 | } 146 | sb.append(Util.firstToUpperCase(names[i])); 147 | } 148 | fieldName = sb.toString(); 149 | } 150 | this.fieldName = fieldName; 151 | } 152 | return this.fieldName; 153 | } 154 | 155 | public void setFieldName(String fieldName) { 156 | this.fieldName = fieldName; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/action/DarrenIOC.java: -------------------------------------------------------------------------------- 1 | package action; 2 | 3 | import Utils.Util; 4 | import View.FindViewByIdDialog; 5 | import com.intellij.openapi.actionSystem.AnAction; 6 | import com.intellij.openapi.actionSystem.AnActionEvent; 7 | import com.intellij.openapi.actionSystem.PlatformDataKeys; 8 | import com.intellij.openapi.editor.CaretModel; 9 | import com.intellij.openapi.editor.Document; 10 | import com.intellij.openapi.editor.Editor; 11 | import com.intellij.openapi.editor.SelectionModel; 12 | import com.intellij.openapi.project.Project; 13 | import com.intellij.openapi.ui.Messages; 14 | import com.intellij.openapi.util.TextRange; 15 | import com.intellij.psi.PsiClass; 16 | import com.intellij.psi.PsiFile; 17 | import com.intellij.psi.search.FilenameIndex; 18 | import com.intellij.psi.search.GlobalSearchScope; 19 | import com.intellij.psi.util.PsiUtilBase; 20 | import com.intellij.psi.xml.XmlFile; 21 | import entity.Element; 22 | import org.apache.http.util.TextUtils; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class DarrenIOC extends AnAction { 28 | private FindViewByIdDialog mDialog; 29 | private String mSelectedText; 30 | 31 | @Override 32 | public void actionPerformed(AnActionEvent e) { 33 | // 获取project 34 | Project project = e.getProject(); 35 | // 获取选中内容 36 | final Editor mEditor = e.getData(PlatformDataKeys.EDITOR); 37 | if (null == mEditor) { 38 | return; 39 | } 40 | SelectionModel model = mEditor.getSelectionModel(); 41 | mSelectedText = model.getSelectedText(); 42 | // 未选中布局内容,显示dialog 43 | if (TextUtils.isEmpty(mSelectedText)) { 44 | // 获取光标所在位置的布局 45 | mSelectedText = getCurrentLayout(mEditor); 46 | if (TextUtils.isEmpty(mSelectedText)) { 47 | mSelectedText = Messages.showInputDialog(project, "布局内容:(不需要输入R.layout.)", "未选中布局内容,请输入layout文件名", Messages.getInformationIcon()); 48 | if (TextUtils.isEmpty(mSelectedText)) { 49 | Util.showPopupBalloon(mEditor, "未输入layout文件名", 5); 50 | return; 51 | } 52 | } 53 | } 54 | // 获取布局文件,通过FilenameIndex.getFilesByName获取 55 | // GlobalSearchScope.allScope(project)搜索整个项目 56 | PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, mSelectedText + ".xml", GlobalSearchScope.allScope(project)); 57 | if (psiFiles.length <= 0) { 58 | Util.showPopupBalloon(mEditor, "未找到选中的布局文件" + mSelectedText, 5); 59 | return; 60 | } 61 | XmlFile xmlFile = (XmlFile) psiFiles[0]; 62 | List elements = new ArrayList<>(); 63 | Util.getIDsFromLayout(xmlFile, elements); 64 | // 将代码写入文件,不允许在主线程中进行实时的文件写入 65 | if (elements.size() != 0) { 66 | PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(mEditor, project); 67 | PsiClass psiClass = Util.getTargetClass(mEditor, psiFile); 68 | // 有的话就创建变量和findViewById 69 | if (mDialog != null && mDialog.isShowing()) { 70 | mDialog.cancelDialog(); 71 | } 72 | mDialog = new FindViewByIdDialog(mEditor, project, psiFile, psiClass, elements, mSelectedText); 73 | mDialog.showDialog(); 74 | } else { 75 | Util.showPopupBalloon(mEditor, "未找到任何Id", 5); 76 | } 77 | } 78 | 79 | /** 80 | * 获取当前光标的layout文件 81 | */ 82 | private String getCurrentLayout(Editor editor) { 83 | Document document = editor.getDocument(); 84 | CaretModel caretModel = editor.getCaretModel(); 85 | int caretOffset = caretModel.getOffset(); 86 | int lineNum = document.getLineNumber(caretOffset); 87 | int lineStartOffset = document.getLineStartOffset(lineNum); 88 | int lineEndOffset = document.getLineEndOffset(lineNum); 89 | String lineContent = document.getText(new TextRange(lineStartOffset, lineEndOffset)); 90 | String layoutMatching = "R.layout."; 91 | if (!TextUtils.isEmpty(lineContent) && lineContent.contains(layoutMatching)) { 92 | // 获取layout文件的字符串 93 | int startPosition = lineContent.indexOf(layoutMatching) + layoutMatching.length(); 94 | int endPosition = lineContent.indexOf(")", startPosition); 95 | String layoutStr = lineContent.substring(startPosition, endPosition); 96 | // 可能是另外一种情况 View.inflate 97 | if (layoutStr.contains(",")) { 98 | endPosition = lineContent.indexOf(",", startPosition); 99 | layoutStr = lineContent.substring(startPosition, endPosition); 100 | } 101 | return layoutStr; 102 | } 103 | return null; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/entity/IdBean.java: -------------------------------------------------------------------------------- 1 | package entity; 2 | 3 | import javax.swing.*; 4 | import javax.swing.border.EmptyBorder; 5 | import java.awt.*; 6 | import java.awt.event.FocusEvent; 7 | import java.awt.event.FocusListener; 8 | 9 | /** 10 | * Created by Darren on 2016/12/14. 11 | */ 12 | public class IdBean extends JPanel { 13 | private JCheckBox mEnableCheckBox; 14 | private JLabel mIdJLabel; 15 | private JCheckBox mClickCheckBox; 16 | private JTextField mFieldJTextField; 17 | 18 | /** 19 | * mEnableCheckBox接口 20 | */ 21 | public interface EnableActionListener { 22 | void setEnable(JCheckBox enableCheckBox,Element element); 23 | } 24 | 25 | private EnableActionListener mEnableListener; 26 | 27 | public void setEnableActionListener(EnableActionListener enableActionListener) { 28 | mEnableListener = enableActionListener; 29 | } 30 | 31 | /** 32 | * mFieldJTextField接口 33 | */ 34 | public interface FieldFocusListener { 35 | void setFieldName(JTextField fieldJTextField); 36 | } 37 | 38 | private FieldFocusListener mFieldFocusListener; 39 | 40 | public void setFieldFocusListener(FieldFocusListener fieldFocusListener) { 41 | mFieldFocusListener = fieldFocusListener; 42 | } 43 | 44 | /** 45 | * mClickCheckBox接口 46 | */ 47 | public interface ClickActionListener { 48 | void setClick(JCheckBox clickCheckBox); 49 | } 50 | 51 | private ClickActionListener mClickListener; 52 | 53 | public void setClickActionListener(ClickActionListener clickListener) { 54 | mClickListener = clickListener; 55 | } 56 | 57 | /** 58 | * 构造方法 59 | * 60 | * @param layout 布局 61 | * @param emptyBorder border 62 | * @param jCheckBox 是否生成+name 63 | * @param jLabelId id 64 | * @param jCheckBoxClick onClick 65 | * @param jTextField 字段名 66 | */ 67 | public IdBean(LayoutManager layout, EmptyBorder emptyBorder, 68 | JCheckBox jCheckBox, JLabel jLabelId, JCheckBox jCheckBoxClick, JTextField jTextField, 69 | Element element) { 70 | super(layout); 71 | initLayout(layout, emptyBorder); 72 | mEnableCheckBox = jCheckBox; 73 | mIdJLabel = jLabelId; 74 | mClickCheckBox = jCheckBoxClick; 75 | mFieldJTextField = jTextField; 76 | initComponent(element); 77 | addComponent(); 78 | } 79 | 80 | /** 81 | * addComponent 82 | */ 83 | private void addComponent() { 84 | this.add(mEnableCheckBox); 85 | this.add(mIdJLabel); 86 | this.add(mClickCheckBox); 87 | this.add(mFieldJTextField); 88 | } 89 | 90 | /** 91 | * 设置Component 92 | */ 93 | private void initComponent(Element element) { 94 | mEnableCheckBox.setSelected(element.isCreateFiled()); 95 | mClickCheckBox.setEnabled(true); 96 | if (element.isCreateClickMethod()) { 97 | mClickCheckBox.setSelected(element.isCreateClickMethod()); 98 | } 99 | 100 | mIdJLabel.setEnabled(element.isCreateFiled()); 101 | mFieldJTextField.setEnabled(element.isCreateFiled()); 102 | 103 | // 设置左对齐 104 | mEnableCheckBox.setHorizontalAlignment(JLabel.LEFT); 105 | mIdJLabel.setHorizontalAlignment(JLabel.LEFT); 106 | mFieldJTextField.setHorizontalAlignment(JTextField.LEFT); 107 | // 监听 108 | mEnableCheckBox.addActionListener(e -> { 109 | if (mEnableListener != null) { 110 | mEnableListener.setEnable(mEnableCheckBox,element); 111 | mIdJLabel.setEnabled(mEnableCheckBox.isSelected()); 112 | mFieldJTextField.setEnabled(mEnableCheckBox.isSelected()); 113 | } 114 | }); 115 | // 监听 116 | mClickCheckBox.addActionListener(e -> { 117 | if (mClickListener != null) { 118 | mClickListener.setClick(mClickCheckBox); 119 | } 120 | }); 121 | // 监听 122 | mFieldJTextField.addFocusListener(new FocusListener() { 123 | @Override 124 | public void focusGained(FocusEvent e) { 125 | if (mFieldFocusListener != null) { 126 | mFieldFocusListener.setFieldName(mFieldJTextField); 127 | } 128 | } 129 | 130 | @Override 131 | public void focusLost(FocusEvent e) { 132 | if (mFieldFocusListener != null) { 133 | mFieldFocusListener.setFieldName(mFieldJTextField); 134 | } 135 | } 136 | }); 137 | } 138 | 139 | /** 140 | * 设置布局相关 141 | * 142 | * @param layout 143 | * @param emptyBorder 144 | */ 145 | private void initLayout(LayoutManager layout, EmptyBorder emptyBorder) { 146 | // 设置布局内容 147 | this.setLayout(layout); 148 | // 设置border 149 | this.setBorder(emptyBorder); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/Utils/ViewFieldMethodCreator.java: -------------------------------------------------------------------------------- 1 | package Utils; 2 | 3 | import View.FindViewByIdDialog; 4 | import com.intellij.codeInsight.actions.ReformatCodeProcessor; 5 | import com.intellij.openapi.command.WriteCommandAction.Simple; 6 | import com.intellij.openapi.editor.Editor; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.psi.*; 9 | import com.intellij.psi.codeStyle.JavaCodeStyleManager; 10 | import com.intellij.psi.search.FilenameIndex; 11 | import com.intellij.psi.search.GlobalSearchScope; 12 | import entity.Element; 13 | import org.apache.http.util.TextUtils; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | public class ViewFieldMethodCreator extends Simple { 19 | 20 | private FindViewByIdDialog mDialog; 21 | private Editor mEditor; 22 | private PsiFile mFile; 23 | private Project mProject; 24 | private PsiClass mClass; 25 | private List mElements; 26 | private PsiElementFactory mFactory; 27 | 28 | public ViewFieldMethodCreator(FindViewByIdDialog dialog, Editor editor, PsiFile psiFile, PsiClass psiClass, String command, List elements, String selectedText) { 29 | super(psiClass.getProject(), command); 30 | mDialog = dialog; 31 | mEditor = editor; 32 | mFile = psiFile; 33 | mProject = psiClass.getProject(); 34 | mClass = psiClass; 35 | mElements = elements; 36 | // 获取Factory 37 | mFactory = JavaPsiFacade.getElementFactory(mProject); 38 | } 39 | 40 | @Override 41 | protected void run() throws Throwable { 42 | try { 43 | generateFields(); 44 | generateOnClickMethod(); 45 | } catch (Exception e) { 46 | // 异常打印 47 | mDialog.cancelDialog(); 48 | Util.showPopupBalloon(mEditor, e.getMessage(), 10); 49 | return; 50 | } 51 | // 重写class 52 | JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(mProject); 53 | styleManager.optimizeImports(mFile); 54 | styleManager.shortenClassReferences(mClass); 55 | new ReformatCodeProcessor(mProject, mClass.getContainingFile(), null, false).runWithoutProgress(); 56 | Util.showPopupBalloon(mEditor, "生成成功", 5); 57 | } 58 | 59 | /** 60 | * 创建变量 61 | */ 62 | private void generateFields() { 63 | for (Element element : mElements) { 64 | if (mClass.getText().contains("@ViewById(" + element.getFullID() + ")")) { 65 | // 不创建新的变量 66 | continue; 67 | } 68 | // 设置变量名,获取text里面的内容 69 | String text = element.getXml().getAttributeValue("android:text"); 70 | if (TextUtils.isEmpty(text)) { 71 | // 如果是text为空,则获取hint里面的内容 72 | text = element.getXml().getAttributeValue("android:hint"); 73 | } 74 | // 如果是@string/app_name类似 75 | if (!TextUtils.isEmpty(text) && text.contains("@string/")) { 76 | text = text.replace("@string/", ""); 77 | // 获取strings.xml 78 | PsiFile[] psiFiles = FilenameIndex.getFilesByName(mProject, "strings.xml", GlobalSearchScope.allScope(mProject)); 79 | if (psiFiles.length > 0) { 80 | for (PsiFile psiFile : psiFiles) { 81 | // 获取src\main\res\values下面的strings.xml文件 82 | String dirName = psiFile.getParent().toString(); 83 | if (dirName.contains("src\\main\\res\\values")) { 84 | text = Util.getTextFromStringsXml(psiFile, text); 85 | } 86 | } 87 | } 88 | } 89 | 90 | StringBuilder fromText = new StringBuilder(); 91 | if (!TextUtils.isEmpty(text)) { 92 | fromText.append("/****" + text + "****/\n"); 93 | } 94 | fromText.append("@ViewById(" + element.getFullID() + ")\n"); 95 | fromText.append("private "); 96 | fromText.append(element.getName()); 97 | fromText.append(" "); 98 | fromText.append(element.getFieldName()); 99 | fromText.append(";"); 100 | // 创建点击方法 101 | if (element.isCreateFiled()) { 102 | // 添加到class 103 | mClass.add(mFactory.createFieldFromText(fromText.toString(), mClass)); 104 | } 105 | } 106 | } 107 | 108 | /** 109 | * 创建OnClick方法 110 | */ 111 | private void generateOnClickMethod() { 112 | for (Element element : mElements) { 113 | // 可以使用并且可以点击 114 | if (element.isCreateClickMethod()) { 115 | // 需要创建OnClick方法 116 | String methodName = getClickMethodName(element) + "Click"; 117 | PsiMethod[] onClickMethods = mClass.findMethodsByName(methodName, true); 118 | boolean clickMethodExist = onClickMethods.length > 0; 119 | if (!clickMethodExist) { 120 | // 创建点击方法 121 | createClickMethod(methodName, element); 122 | } 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * 创建一个点击事件 129 | */ 130 | private void createClickMethod(String methodName, Element element) { 131 | // 拼接方法的字符串 132 | StringBuilder methodBuilder = new StringBuilder(); 133 | methodBuilder.append("@OnClick(" + element.getFullID() + ")\n"); 134 | methodBuilder.append("private void " + methodName + "(" + element.getName() + " " + getClickMethodName(element) + "){"); 135 | methodBuilder.append("\n}"); 136 | // 创建OnClick方法 137 | mClass.add(mFactory.createMethodFromText(methodBuilder.toString(), mClass)); 138 | } 139 | 140 | /** 141 | * 获取点击方法的名称 142 | */ 143 | public String getClickMethodName(Element element) { 144 | String[] names = element.getId().split("_"); 145 | // aaBbCc 146 | StringBuilder sb = new StringBuilder(); 147 | for (int i = 0; i < names.length; i++) { 148 | if (i == 0) { 149 | sb.append(names[i]); 150 | } else { 151 | sb.append(Util.firstToUpperCase(names[i])); 152 | } 153 | } 154 | return sb.toString(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/Utils/Util.java: -------------------------------------------------------------------------------- 1 | package Utils; 2 | 3 | import com.intellij.openapi.application.ApplicationManager; 4 | import com.intellij.openapi.editor.Editor; 5 | import com.intellij.openapi.project.Project; 6 | import com.intellij.openapi.ui.popup.Balloon; 7 | import com.intellij.openapi.ui.popup.JBPopupFactory; 8 | import com.intellij.psi.*; 9 | import com.intellij.psi.search.EverythingGlobalScope; 10 | import com.intellij.psi.search.FilenameIndex; 11 | import com.intellij.psi.search.GlobalSearchScope; 12 | import com.intellij.psi.util.PsiTreeUtil; 13 | import com.intellij.psi.xml.XmlAttribute; 14 | import com.intellij.psi.xml.XmlFile; 15 | import com.intellij.psi.xml.XmlTag; 16 | import com.intellij.ui.JBColor; 17 | import entity.Element; 18 | import org.apache.http.util.TextUtils; 19 | 20 | import java.awt.*; 21 | import java.util.Locale; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | public class Util { 26 | // 通过strings.xml获取的值 27 | private static String StringValue; 28 | 29 | /** 30 | * 显示dialog 31 | * 32 | * @param editor 33 | * @param result 内容 34 | * @param time 显示时间,单位秒 35 | */ 36 | public static void showPopupBalloon(final Editor editor, final String result, final int time) { 37 | ApplicationManager.getApplication().invokeLater(new Runnable() { 38 | public void run() { 39 | JBPopupFactory factory = JBPopupFactory.getInstance(); 40 | factory.createHtmlTextBalloonBuilder(result, null, new JBColor(new Color(116, 214, 238), new Color(76, 112, 117)), null) 41 | .setFadeoutTime(time * 1000) 42 | .createBalloon() 43 | .show(factory.guessBestPopupLocation(editor), Balloon.Position.below); 44 | } 45 | }); 46 | } 47 | 48 | /** 49 | * 驼峰 50 | * 51 | * @param fieldName 52 | * @return 53 | */ 54 | public static String getFieldName(String fieldName) { 55 | if (!TextUtils.isEmpty(fieldName)) { 56 | String[] names = fieldName.split("_"); 57 | StringBuilder sb = new StringBuilder(); 58 | for (int i = 0; i < names.length; i++) { 59 | sb.append(firstToUpperCase(names[i])); 60 | } 61 | fieldName = sb.toString(); 62 | } 63 | return fieldName; 64 | } 65 | 66 | /** 67 | * 第一个字母大写 68 | * 69 | * @param key 70 | * @return 71 | */ 72 | public static String firstToUpperCase(String key) { 73 | return key.substring(0, 1).toUpperCase(Locale.CHINA) + key.substring(1); 74 | } 75 | 76 | /** 77 | * 解析xml获取string的值 78 | * 79 | * @param psiFile 80 | * @param text 81 | * @return 82 | */ 83 | public static String getTextFromStringsXml(PsiFile psiFile, String text) { 84 | psiFile.accept(new XmlRecursiveElementVisitor() { 85 | @Override 86 | public void visitElement(PsiElement element) { 87 | super.visitElement(element); 88 | if (element instanceof XmlTag) { 89 | XmlTag tag = (XmlTag) element; 90 | if (tag.getName().equals("string") 91 | && tag.getAttributeValue("name").equals(text)) { 92 | PsiElement[] children = tag.getChildren(); 93 | String value = ""; 94 | for (PsiElement child : children) { 95 | value += child.getText(); 96 | } 97 | // value = My Application 98 | // 用正则获取值 99 | Pattern p = Pattern.compile("(.*)"); 100 | Matcher m = p.matcher(value); 101 | while (m.find()) { 102 | StringValue = m.group(1); 103 | } 104 | } 105 | } 106 | } 107 | }); 108 | return StringValue; 109 | } 110 | 111 | /** 112 | * 获取所有id 113 | * 114 | * @param file 115 | * @param elements 116 | * @return 117 | */ 118 | public static java.util.List getIDsFromLayout(final PsiFile file, final java.util.List elements) { 119 | // To iterate over the elements in a file 120 | // 遍历一个文件的所有元素 121 | file.accept(new XmlRecursiveElementVisitor() { 122 | @Override 123 | public void visitElement(PsiElement element) { 124 | super.visitElement(element); 125 | // 解析Xml标签 126 | if (element instanceof XmlTag) { 127 | XmlTag tag = (XmlTag) element; 128 | // 获取Tag的名字(TextView)或者自定义 129 | String name = tag.getName(); 130 | // 如果有include 131 | if (name.equalsIgnoreCase("include")) { 132 | // 获取布局 133 | XmlAttribute layout = tag.getAttribute("layout", null); 134 | // 获取project 135 | Project project = file.getProject(); 136 | // 布局文件 137 | XmlFile include = null; 138 | PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, getLayoutName(layout.getValue()) + ".xml", GlobalSearchScope.allScope(project)); 139 | if (psiFiles.length > 0) { 140 | include = (XmlFile) psiFiles[0]; 141 | } 142 | if (include != null) { 143 | // 递归 144 | getIDsFromLayout(include, elements); 145 | return; 146 | } 147 | } 148 | // 获取id字段属性 149 | XmlAttribute id = tag.getAttribute("android:id", null); 150 | if (id == null) { 151 | return; 152 | } 153 | // 获取id的值 154 | String idValue = id.getValue(); 155 | if (idValue == null) { 156 | return; 157 | } 158 | XmlAttribute aClass = tag.getAttribute("class", null); 159 | if (aClass != null) { 160 | name = aClass.getValue(); 161 | } 162 | // 添加到list 163 | try { 164 | Element e = new Element(name, idValue, tag); 165 | elements.add(e); 166 | } catch (IllegalArgumentException e) { 167 | 168 | } 169 | } 170 | } 171 | }); 172 | 173 | 174 | return elements; 175 | } 176 | 177 | /** 178 | * layout.getValue()返回的值为@layout/layout_view 179 | * 180 | * @param layout 181 | * @return 182 | */ 183 | public static String getLayoutName(String layout) { 184 | if (layout == null || !layout.startsWith("@") || !layout.contains("/")) { 185 | return null; 186 | } 187 | // @layout layout_view 188 | String[] parts = layout.split("/"); 189 | if (parts.length != 2) { 190 | return null; 191 | } 192 | // layout_view 193 | return parts[1]; 194 | } 195 | 196 | /** 197 | * 根据当前文件获取对应的class文件 198 | * 199 | * @param editor 200 | * @param file 201 | * @return 202 | */ 203 | public static PsiClass getTargetClass(Editor editor, PsiFile file) { 204 | int offset = editor.getCaretModel().getOffset(); 205 | PsiElement element = file.findElementAt(offset); 206 | if (element == null) { 207 | return null; 208 | } else { 209 | PsiClass target = PsiTreeUtil.getParentOfType(element, PsiClass.class); 210 | return target instanceof SyntheticElement ? null : target; 211 | } 212 | } 213 | 214 | 215 | /** 216 | * 获取initView方法里面的每条数据 217 | * 218 | * @param mClass 219 | * @return 220 | */ 221 | public static PsiStatement[] getInitViewBodyStatements(PsiClass mClass) { 222 | // 获取initView方法 223 | PsiMethod[] method = mClass.findMethodsByName("initView", false); 224 | PsiStatement[] statements = null; 225 | if (method.length > 0 && method[0].getBody() != null) { 226 | PsiCodeBlock methodBody = method[0].getBody(); 227 | statements = methodBody.getStatements(); 228 | } 229 | return statements; 230 | } 231 | 232 | 233 | /** 234 | * 获取onClick方法里面的每条数据 235 | * 236 | * @param mClass 237 | * @return 238 | */ 239 | public static PsiElement[] getOnClickStatement(PsiClass mClass) { 240 | // 获取onClick方法 241 | PsiMethod[] onClickMethods = mClass.findMethodsByName("onClick", false); 242 | PsiElement[] psiElements = null; 243 | if (onClickMethods.length > 0 && onClickMethods[0].getBody() != null) { 244 | PsiCodeBlock onClickMethodBody = onClickMethods[0].getBody(); 245 | psiElements = onClickMethodBody.getChildren(); 246 | } 247 | return psiElements; 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/View/FindViewByIdDialog.java: -------------------------------------------------------------------------------- 1 | package View; 2 | 3 | import Utils.Util; 4 | import Utils.ViewFieldMethodCreator; 5 | import com.intellij.openapi.editor.Editor; 6 | import com.intellij.openapi.project.Project; 7 | import com.intellij.psi.*; 8 | import com.intellij.ui.components.JBScrollPane; 9 | import entity.Element; 10 | import entity.IdBean; 11 | 12 | import javax.swing.*; 13 | import javax.swing.border.EmptyBorder; 14 | import java.awt.*; 15 | import java.awt.event.ActionEvent; 16 | import java.awt.event.ActionListener; 17 | import java.util.List; 18 | 19 | /** 20 | * Created by wangzai on 2016/11/24. 21 | */ 22 | public class FindViewByIdDialog extends JFrame implements ActionListener, IdBean.EnableActionListener { 23 | private String mTitle = "FindViewByIdDialog"; 24 | private Project mProject; 25 | private Editor mEditor; 26 | private String mSelectedText; 27 | private List mElements; 28 | // 获取当前文件 29 | private PsiFile mPsiFile; 30 | // 获取class 31 | private PsiClass mClass; 32 | 33 | 34 | // 标签JPanel 35 | private JPanel mPanelTitle = new JPanel(); 36 | private JLabel mTitleId = new JLabel("ViewId"); 37 | private JLabel mTitleClick = new JLabel("OnClick"); 38 | private JLabel mTitleField = new JLabel("ViewFiled"); 39 | 40 | // 内容JPanel 41 | private JPanel mContentJPanel = new JPanel(); 42 | private GridBagLayout mContentLayout = new GridBagLayout(); 43 | private GridBagConstraints mContentConstraints = new GridBagConstraints(); 44 | // 内容JBScrollPane滚动 45 | private JBScrollPane jScrollPane; 46 | 47 | // 底部JPanel 48 | // LayoutInflater JPanel 49 | private JPanel mPanelInflater = new JPanel(new FlowLayout(FlowLayout.LEFT)); 50 | // 是否全选 51 | private JCheckBox mCheckAll = new JCheckBox("ViewWidget"); 52 | // 确定、取消JPanel 53 | private JPanel mPanelButtonRight = new JPanel(); 54 | private JButton mButtonConfirm = new JButton("确定"); 55 | private JButton mButtonCancel = new JButton("取消"); 56 | 57 | // GridBagLayout不要求组件的大小相同便可以将组件垂直、水平或沿它们的基线对齐 58 | private GridBagLayout mLayout = new GridBagLayout(); 59 | // GridBagConstraints用来控制添加进的组件的显示位置 60 | private GridBagConstraints mConstraints = new GridBagConstraints(); 61 | 62 | public FindViewByIdDialog(Editor editor, Project project, PsiFile psiFile, PsiClass psiClass, List elements, String selectedText) { 63 | mEditor = editor; 64 | mProject = project; 65 | mSelectedText = selectedText; 66 | mElements = elements; 67 | mPsiFile = psiFile; 68 | mClass = psiClass; 69 | initTopPanel(); 70 | initExist(); 71 | initContentPanel(); 72 | initBottomPanel(); 73 | setConstraints(); 74 | setDialog(); 75 | } 76 | 77 | /** 78 | * 判断已存在的变量,设置全选 79 | * 判断onclick是否写入 80 | */ 81 | private void initExist() { 82 | // 判断是否全选 记录当前可用的个数 83 | int mCurrentAbleSize = 0; 84 | // 判断是否已存在的变量 85 | boolean isFdExist = false; 86 | 87 | for (Element element : mElements) { 88 | // 判断ViewById是否存在 89 | if (mClass.getText().contains("@ViewById(" + element.getFullID() + ")")) { 90 | isFdExist = true; 91 | } else { 92 | isFdExist = false; 93 | } 94 | 95 | // 如果当前没有该属性注解存在 96 | if (!isFdExist) { 97 | mCurrentAbleSize++; 98 | element.setIsCreateFiled(true); 99 | } else { 100 | element.setIsCreateFiled(false); 101 | } 102 | 103 | mCheckAll.setSelected(mCurrentAbleSize == mElements.size()); 104 | mCheckAll.addActionListener(this); 105 | } 106 | } 107 | 108 | /** 109 | * 添加头部 110 | */ 111 | private void initTopPanel() { 112 | mPanelTitle.setLayout(new GridLayout(1, 4, 10, 10)); 113 | mPanelTitle.setBorder(new EmptyBorder(5, 10, 5, 10)); 114 | mTitleId.setHorizontalAlignment(JLabel.LEFT); 115 | mTitleClick.setHorizontalAlignment(JLabel.LEFT); 116 | mTitleField.setHorizontalAlignment(JLabel.LEFT); 117 | // 添加到JPanel 118 | mPanelTitle.add(mCheckAll); 119 | mPanelTitle.add(mTitleId); 120 | mPanelTitle.add(mTitleClick); 121 | mPanelTitle.add(mTitleField); 122 | mPanelTitle.setSize(720, 30); 123 | // 添加到JFrame 124 | getContentPane().add(mPanelTitle, 0); 125 | } 126 | 127 | /** 128 | * 添加底部 129 | */ 130 | private void initBottomPanel() { 131 | // 添加监听 132 | mButtonConfirm.addActionListener(this); 133 | mButtonCancel.addActionListener(this); 134 | // 右边 135 | mPanelButtonRight.add(mButtonConfirm); 136 | mPanelButtonRight.add(mButtonCancel); 137 | // 添加到JFrame 138 | getContentPane().add(mPanelInflater, 2); 139 | getContentPane().add(mPanelButtonRight, 3); 140 | } 141 | 142 | /** 143 | * 解析mElements,并添加到JPanel 144 | */ 145 | private void initContentPanel() { 146 | mContentJPanel.removeAll(); 147 | // 设置内容 148 | for (int i = 0; i < mElements.size(); i++) { 149 | Element mElement = mElements.get(i); 150 | IdBean itemJPanel = new IdBean(new GridLayout(1, 4, 10, 10), 151 | new EmptyBorder(5, 10, 5, 10), 152 | new JCheckBox(mElement.getName()), 153 | new JLabel(mElement.getId()), 154 | new JCheckBox(), 155 | new JTextField(mElement.getFieldName()), 156 | mElement); 157 | // 监听 158 | itemJPanel.setEnableActionListener(this); 159 | itemJPanel.setClickActionListener(clickCheckBox -> mElement.setIsCreateClickMethod(clickCheckBox.isSelected())); 160 | itemJPanel.setFieldFocusListener(fieldJTextField -> mElement.setFieldName(fieldJTextField.getText())); 161 | mContentJPanel.add(itemJPanel); 162 | mContentConstraints.fill = GridBagConstraints.HORIZONTAL; 163 | mContentConstraints.gridwidth = 0; 164 | mContentConstraints.gridx = 0; 165 | mContentConstraints.gridy = i; 166 | mContentConstraints.weightx = 1; 167 | mContentLayout.setConstraints(itemJPanel, mContentConstraints); 168 | } 169 | mContentJPanel.setLayout(mContentLayout); 170 | jScrollPane = new JBScrollPane(mContentJPanel); 171 | jScrollPane.revalidate(); 172 | // 添加到JFrame 173 | getContentPane().add(jScrollPane, 1); 174 | } 175 | 176 | /** 177 | * 设置Constraints 178 | */ 179 | private void setConstraints() { 180 | // 使组件完全填满其显示区域 181 | mConstraints.fill = GridBagConstraints.BOTH; 182 | // 设置组件水平所占用的格子数,如果为0,就说明该组件是该行的最后一个 183 | mConstraints.gridwidth = 0; 184 | // 第几列 185 | mConstraints.gridx = 0; 186 | // 第几行 187 | mConstraints.gridy = 0; 188 | // 行拉伸0不拉伸,1完全拉伸 189 | mConstraints.weightx = 1; 190 | // 列拉伸0不拉伸,1完全拉伸 191 | mConstraints.weighty = 0; 192 | // 设置组件 193 | mLayout.setConstraints(mPanelTitle, mConstraints); 194 | mConstraints.fill = GridBagConstraints.BOTH; 195 | mConstraints.gridwidth = 1; 196 | mConstraints.gridx = 0; 197 | mConstraints.gridy = 1; 198 | mConstraints.weightx = 1; 199 | mConstraints.weighty = 1; 200 | mLayout.setConstraints(jScrollPane, mConstraints); 201 | mConstraints.fill = GridBagConstraints.HORIZONTAL; 202 | mConstraints.gridwidth = 0; 203 | mConstraints.gridx = 0; 204 | mConstraints.gridy = 2; 205 | mConstraints.weightx = 1; 206 | mConstraints.weighty = 0; 207 | mLayout.setConstraints(mPanelInflater, mConstraints); 208 | mConstraints.fill = GridBagConstraints.NONE; 209 | mConstraints.gridwidth = 0; 210 | mConstraints.gridx = 0; 211 | mConstraints.gridy = 3; 212 | mConstraints.weightx = 0; 213 | mConstraints.weighty = 0; 214 | mConstraints.anchor = GridBagConstraints.EAST; 215 | mLayout.setConstraints(mPanelButtonRight, mConstraints); 216 | } 217 | 218 | /** 219 | * 显示dialog 220 | */ 221 | public void showDialog() { 222 | // 显示 223 | setVisible(true); 224 | } 225 | 226 | /** 227 | * 设置JFrame参数 228 | */ 229 | private void setDialog() { 230 | // 设置标题 231 | setTitle(mTitle); 232 | // 设置布局管理 233 | setLayout(mLayout); 234 | // 不可拉伸 235 | setResizable(false); 236 | // 设置大小 237 | setSize(720, 405); 238 | // 自适应大小 239 | // pack(); 240 | // 设置居中,放在setSize后面 241 | setLocationRelativeTo(null); 242 | // 显示最前 243 | setAlwaysOnTop(true); 244 | } 245 | 246 | /** 247 | * 关闭dialog 248 | */ 249 | public void cancelDialog() { 250 | setVisible(false); 251 | dispose(); 252 | } 253 | 254 | @Override 255 | public void actionPerformed(ActionEvent e) { 256 | switch (e.getActionCommand()) { 257 | case "确定": 258 | cancelDialog(); 259 | setCreator(); 260 | break; 261 | case "取消": 262 | cancelDialog(); 263 | break; 264 | case "ViewWidget": 265 | // 刷新 266 | for (Element mElement : mElements) { 267 | mElement.setIsCreateFiled(mCheckAll.isSelected()); 268 | } 269 | remove(jScrollPane); 270 | initContentPanel(); 271 | setConstraints(); 272 | revalidate(); 273 | break; 274 | } 275 | } 276 | 277 | /** 278 | * 生成 279 | */ 280 | private void setCreator() { 281 | new ViewFieldMethodCreator(this, mEditor, mPsiFile, mClass, 282 | "Generate Injections", mElements, mSelectedText) 283 | .execute(); 284 | } 285 | 286 | /** 287 | * 更新所有选中的CheckBox 288 | */ 289 | private void updateAllSelectCb() { 290 | boolean isAllSelect = true; 291 | for (Element element : mElements) { 292 | if (!element.isCreateFiled()) { 293 | isAllSelect = false; 294 | break; 295 | } 296 | } 297 | mCheckAll.setSelected(isAllSelect); 298 | } 299 | 300 | @Override 301 | public void setEnable(JCheckBox enableCheckBox, Element element) { 302 | element.setIsCreateFiled(enableCheckBox.isSelected()); 303 | updateAllSelectCb(); 304 | } 305 | } 306 | --------------------------------------------------------------------------------