├── .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 | 
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 |
--------------------------------------------------------------------------------