├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── dictionaries
│ └── pc.xml
├── markdown-exported-files.xml
├── markdown-navigator.xml
├── markdown-navigator
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── uiDesigner.xml
└── vcs.xml
├── GenerateButterKnife.gif
├── GenerateButterKnife1.gif
├── GenerateFindViewById.gif
├── GenerateFindViewById.iml
├── GenerateFindViewById.jar
├── README.md
├── resources
└── META-INF
│ └── plugin.xml
└── src
├── EditorExt.kt
├── Ext.kt
├── ListExt.kt
├── ProjectExt.kt
├── PsiClass.kt
├── PsiFileExt.kt
├── StringExt.kt
├── actions
├── ButterKnifeAction.kt
└── FindViewByIdAction.kt
├── constant
└── Constant.kt
├── entitys
├── Element.kt
└── IdBean.kt
├── utils
├── CreateMethodCreator.kt
└── GenerateCreator.kt
└── views
├── ButterKnifeDialog.kt
├── FindViewByIdDialog.kt
└── GenerateDialog.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /out
3 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/dictionaries/pc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/markdown-exported-files.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.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 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GenerateButterKnife.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangzailfm/GenerateFindViewById/4f180c4b30f8c0cfe855fbcb08507d89fa3f78ed/GenerateButterKnife.gif
--------------------------------------------------------------------------------
/GenerateButterKnife1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangzailfm/GenerateFindViewById/4f180c4b30f8c0cfe855fbcb08507d89fa3f78ed/GenerateButterKnife1.gif
--------------------------------------------------------------------------------
/GenerateFindViewById.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangzailfm/GenerateFindViewById/4f180c4b30f8c0cfe855fbcb08507d89fa3f78ed/GenerateFindViewById.gif
--------------------------------------------------------------------------------
/GenerateFindViewById.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/GenerateFindViewById.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wangzailfm/GenerateFindViewById/4f180c4b30f8c0cfe855fbcb08507d89fa3f78ed/GenerateFindViewById.jar
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GenerateFindViewById
2 |
3 | ## 演示
4 | FindViewById
5 | 
6 |
7 | ButterKnife
8 | 
9 | 
10 |
11 |
12 |
13 | ## 更新
14 | - 1.4.2 Fix bugs with the same xml name for more than one Module.
15 | - 1.4.1 Code replace to Kotlin, add Forced Casts option, default true, and Replace code to Kotlin language.[Revision 26.0.0 Beta 1 Important changes](https://developer.android.google.cn/topic/libraries/support-library/revisions.html#26-0-0-beta1)
16 | - 1.4 Variable names only support mAaBbCc and aaBbCc naming formats, The switch block adds the default statement.[Alibaba Java Coding Guidelines](https://github.com/alibaba/p3c)
17 | - 1.3 Variable names support three naming formats: 1, mAaBbCc, 2, aa_bb_cc, 3, aaBbCc
18 | - 1.2.2 Onclick can select all or deselect all
19 | - 1.2.1 Fix ImageView and TextView can not click the OnClick option
20 | - 1.2 Support ViewHolder
21 | - 1.1.2 Button defaults to the OnClick option
22 | - 1.1 Support ButterKnife, version 8.4.0, shortcut keys Ctrl+Alt+Shift+E
23 | - 1.0 Support Activity and Fragment, shortcut keys Ctrl+Alt+E
24 |
25 |
26 | ## 安装
27 | - 下载项目里面的`GenerateFindViewById.ja`r包,然后打开AS的Plugins,点击`Install plugin from disk...`安装
28 | - 打开AS的Plugins,点击`Browse repositories...`然后搜索`GenerateFindViewById`,然后安装
29 |
30 | ## 快捷键
31 | - FindViewById:`Ctrl+Alt+E`
32 | - ButterKnife:`Ctrl+Alt+Shift+E`
33 |
34 | ## 说明
35 | - 可输入布局字段,可选中布局文件字段,自动生成有id控件相应的代码
36 | - `Activity`如果没有`onCreate`方法,会先生成`onCreate`方法,再重新操作一次才生成有id控件相应的代码。
37 | - `Fragment`如果没有`onCreateView`方法,会先生成`onCreateView`方法,再重新操作一次才生成有id控件相应的代码。
38 | - 可选生成的字段,可编辑变量名,可选择是否`LayoutInflater`类型。
39 | - `LayoutInflater`类型生成的变量规则,如`LayoutInflater`的变量为`mView`,生成控件变量后面会加上`View`。
40 |
41 | ## 用法
42 | 1. 新建`Activity`或者`Fragment`后,选中布局按下快捷键`Alt+Insert`,然后选择`FindViewById`或者在菜单栏中的`Code`中选择`FindViewById`,或者直接使用快捷键`Ctrl+Alt+E`
43 | 2. 如果没有选中布局,会弹出输入框,输入布局,插件会自动遍历布局列出所有带id的控件
44 | 3. 会自动检测是否已有代码,可选择是否生成、是否生成`OnClick`代码,可编辑变量名
45 | 4. 可选择是否生成`View view = LayoutInflater.from(context).inflater()`代码,可编辑生成的`View`的变量名
46 | 5. 点击确认生成
47 |
48 | ## ButterKnife用法
49 | 1. 新建`Activity`或者`Fragment`后,选中布局按下快捷键`Alt+Insert`,然后选择`ButterKnife`或者在菜单栏中的`Code`中选择`ButterKnife`,或者直接使用快捷键`Ctrl+Alt+Shift+E`
50 | 2. 如果没有选中布局,会弹出输入框,输入布局,插件会自动遍历布局列出所有带id的控件
51 | 3. 会自动检测是否已有代码,可选择是否生成、是否生成`OnClick`代码,可编辑变量名
52 | 4. 可选择是否生成`View view = LayoutInflater.from(context).inflater()`代码,可编辑生成的`View`的变量名
53 | 5. 点击确认生成
54 |
55 | ## 规则
56 | 1. `Activity`如果没有`onCreate`方法,会先生成`onCreate`方法,`Fragment`如果没有`onCreateView`方法,会先生成`onCreateView`方法
57 | 2. 没有id的控件是不会识别到的
58 | 3. 识别到的控件变量名为`mAaBbCc`命名
59 | 4. 识别到的控件中有`clickable = true`属性,自动生成`setOnClickListener`代码和`onClick`方法
60 | 5. 识别到的控件`ButterKnife`都可以生成到`onClick`方法
61 | 6. 自动识别布局中的`include`标签, 读取对应布局中的控件
62 | 7. 识别到的控件中有`text`或者`hint`属性,会自动生成里面的值到字段注释
63 | 8. `LayoutInflater`生成的变量名规则为`mAaBbCc+View`的变量名(如`mView`会去掉`m`)
64 | 9. `ButterKnife`生成的`LayoutInflater`是`ButterKnife.findById()`来替换`view.findViewById()`
65 |
66 | ## License
67 | ```
68 | Copyright 2016 Jowan
69 |
70 | Licensed under the Apache License, Version 2.0 (the "License");
71 | you may not use this file except in compliance with the License.
72 | You may obtain a copy of the License at
73 |
74 | http://www.apache.org/licenses/LICENSE-2.0
75 |
76 | Unless required by applicable law or agreed to in writing, software
77 | distributed under the License is distributed on an "AS IS" BASIS,
78 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
79 | See the License for the specific language governing permissions and
80 | limitations under the License.
81 | ```
--------------------------------------------------------------------------------
/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.wangzai.plugin.findViewById
3 | GenerateFindViewById
4 | 1.4.3
5 | GitHub
6 |
7 |
9 | If there is a problem, please go to my GitHub above issues.
10 | ]]>
11 |
12 |
14 | 1.4.2 Fix bugs with the same xml name for more than one Module.
15 | 1.4.1 Code replace to Kotlin, add Forced Casts option, default true, and Replace code to Kotlin language.
16 | Revision 26.0.0 Beta 1 Important changes
17 | 1.4 Variable names only support mAaBbCc and aaBbCc naming formats, The switch block adds the default statement.
18 | Alibaba Java Coding Guidelines
19 | 1.3 Variable names support three naming formats: 1, mAaBbCc, 2, aa_bb_cc, 3, aaBbCc
20 | 1.2.2 Onclick can select all or deselect all
21 | 1.2.1 Fix ImageView and TextView can not click the OnClick option
22 | 1.2 Support ViewHolder
23 | 1.1.2 Button defaults to the OnClick option
24 | 1.1 Support ButterKnife, version 8.4.0, shortcut keys Ctrl+Alt+Shift+E
25 | 1.0 Support Activity and Fragment, shortcut keys Ctrl+Alt+E
26 | ]]>
27 |
28 |
29 |
30 |
31 |
32 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
46 |
47 |
48 |
49 |
50 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/src/EditorExt.kt:
--------------------------------------------------------------------------------
1 | import com.intellij.openapi.application.ApplicationManager
2 | import com.intellij.openapi.editor.Editor
3 | import com.intellij.openapi.ui.popup.Balloon
4 | import com.intellij.openapi.ui.popup.JBPopupFactory
5 | import com.intellij.psi.PsiClass
6 | import com.intellij.psi.PsiFile
7 | import com.intellij.psi.SyntheticElement
8 | import com.intellij.psi.util.PsiTreeUtil
9 | import com.intellij.ui.JBColor
10 | import constant.Constant
11 | import java.awt.Color
12 |
13 |
14 | /**
15 | * 显示dialog
16 | * @param editor editor
17 | *
18 | * @param result 内容
19 | *
20 | * @param time 显示时间,单位秒
21 | */
22 | fun Editor.showPopupBalloon(result: String?, time: Int) {
23 | ApplicationManager.getApplication().invokeLater {
24 | val factory = JBPopupFactory.getInstance()
25 | factory.createHtmlTextBalloonBuilder(result ?: Constant.Ext.UNKNOWN_ERROR, null, JBColor(Color(116, 214, 238), Color(76, 112, 117)), null).setFadeoutTime((time * 1000).toLong()).createBalloon().show(factory.guessBestPopupLocation(this), Balloon.Position.below)
26 | }
27 | result?.outInfo()
28 | }
29 |
30 | /**
31 | * 根据当前文件获取对应的class文件
32 | * @param editor editor
33 | *
34 | * @param file file
35 | *
36 | * @return PsiClass
37 | */
38 | infix fun Editor.getTargetClass(file: PsiFile?): PsiClass? {
39 | val offset = this.caretModel.offset
40 | val element = file?.findElementAt(offset) ?: return null
41 | val target = PsiTreeUtil.getParentOfType(element, PsiClass::class.java)
42 | return if (target is SyntheticElement) null else target
43 | }
--------------------------------------------------------------------------------
/src/Ext.kt:
--------------------------------------------------------------------------------
1 | import com.intellij.psi.*
2 | import entitys.Element
3 | import org.apache.commons.lang.StringUtils
4 | import java.util.*
5 |
6 |
7 | /**
8 | * 创建onCreateView方法
9 | *
10 | * @param mIsButterKnife mIsButterKnife
11 | *
12 | * @return String
13 | */
14 | fun createOnCreateViewMethod(mIsButterKnife: Boolean): String {
15 | val method = StringBuilder()
16 | method.append("@Nullable @Override public View onCreateView(android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup container, @Nullable android.os.Bundle savedInstanceState) {\n")
17 | method.append("\t// TODO:OnCreateView Method has been created, run ")
18 | method.append(if (!mIsButterKnife) "FindViewById" else "ButterKnife")
19 | method.append(" again to generate code\n")
20 | method.append(if (!mIsButterKnife) "\t\tinitView(view);\n" else "\t\tunbinder = ButterKnife.bind(this, view);\n")
21 | method.append("return view;")
22 | method.append("}\n")
23 | return method.toString()
24 | }
25 |
26 | /**
27 | * 创建initView方法,Fragment
28 |
29 | * @return String
30 | */
31 | fun createFragmentInitViewMethod(): String = "public void initView(View view) {\n}"
32 |
33 | /**
34 | * 创建initView方法
35 | *
36 | * @return String
37 | */
38 | fun createInitViewMethod(): String = "public void initView() {\n}"
39 |
40 | /**
41 | * 创建OnDestroyView方法,里面包含unbinder.unbind()
42 | *
43 | * @return String
44 | */
45 | fun createOnDestroyViewMethod(): String = "@Override public void onDestroyView() {\n" +
46 | "\tsuper.onDestroyView();" +
47 | "\tunbinder.unbind();" +
48 | "}"
49 |
50 | /**
51 | * 判断是否实现了OnClickListener接口
52 | * @param referenceElements referenceElements
53 | *
54 | * @return boolean
55 | */
56 | fun isImplementsOnClickListener(referenceElements: Array): Boolean {
57 | referenceElements.forEach {
58 | if (it.text.contains("OnClickListener")) {
59 | return true
60 | }
61 | }
62 | return false
63 | }
64 |
65 |
66 | /**
67 | * 获取OnClickList里面的id集合
68 | *
69 | * @param mOnClickList clickable的Element集合
70 | *
71 | * @return List
72 | */
73 | fun getOnClickListById(mOnClickList: ArrayList): ArrayList {
74 | val list: ArrayList = ArrayList()
75 | mOnClickList.forEach { list.add(it.fullID) }
76 | return list
77 | }
78 |
79 | /**
80 | * 获取注解里面跟OnClickList的id集合
81 | *
82 | * @param annotationList OnClick注解里面的id集合
83 | *
84 | * @param onClickIdList clickable的Element集合
85 | *
86 | * @return List
87 | */
88 | fun createOnClickValue(annotationList: ArrayList, onClickIdList: ArrayList): ArrayList {
89 | onClickIdList
90 | .filterNot { annotationList.contains(it) }
91 | .forEach { annotationList.add(it) }
92 | return annotationList
93 | }
94 |
95 |
96 | /**
97 | * FindViewById,创建findViewById代码到initView方法里面
98 | *
99 | * @param findPre Fragment的话要view.findViewById
100 | *
101 | * @param mIsLayoutInflater 是否选中LayoutInflater
102 | *
103 | * @param mLayoutInflaterText 选中的布局的变量名
104 | *
105 | * @param context context
106 | *
107 | * @param mSelectedText 选中的布局
108 | *
109 | * @param mElements Element的List
110 | *
111 | * @param mLayoutInflaterType type
112 | *
113 | * @param mNeedCast 是否需要强转
114 | *
115 | * @return String
116 | */
117 | fun createFieldsByInitViewMethod(findPre: String?, mIsLayoutInflater: Boolean,
118 | mLayoutInflaterText: String, context: String,
119 | mSelectedText: String, mElements: ArrayList,
120 | mLayoutInflaterType: Int, mNeedCast: Boolean): String {
121 | val initView = StringBuilder()
122 | initView.append(if (StringUtils.isEmpty(findPre)) "private void initView() {\n" else "private void initView(View $findPre) {\n")
123 | if (mIsLayoutInflater) {
124 | // 添加LayoutInflater.from(this).inflate(R.layout.activity_main, null);
125 | val layoutInflater = "$mLayoutInflaterText = LayoutInflater.from($context).inflate(R.layout.$mSelectedText, null);\n"
126 | initView.append(layoutInflater)
127 | }
128 | mElements.filter(Element::isEnable).forEach {
129 | with(it) {
130 | var pre = findPre?.let { findPre + "." } ?: ""
131 | val inflater = if (mIsLayoutInflater) layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType) else ""
132 | pre = if (!mIsLayoutInflater) pre else mLayoutInflaterText + "."
133 | initView.append("$fieldName$inflater = ")
134 | if (mNeedCast) {
135 | initView.append("($name)")
136 | }
137 | initView.append("${pre}findViewById($fullID);\n")
138 | if (isClickable && isClickEnable) {
139 | initView.append("$fieldName$inflater.setOnClickListener(this);\n")
140 | }
141 | }
142 | }
143 | initView.append("}\n")
144 | return initView.toString()
145 | }
146 |
147 |
148 | /**
149 | * ButterKnife,创建findById代码到init方法里面
150 | *
151 | * @param mIsLayoutInflater mIsLayoutInflater
152 | *
153 | * @param mLayoutInflaterText mLayoutInflaterText
154 | *
155 | * @param context context
156 | *
157 | * @param mSelectedText mSelectedText
158 | *
159 | * @param mElements mElements
160 | *
161 | * @param viewMethodName viewMethodName
162 | *
163 | * @return String
164 | */
165 | fun createButterKnifeViewMethod(mIsLayoutInflater: Boolean,
166 | mLayoutInflaterText: String, context: String, mSelectedText:
167 | String, mElements: ArrayList, viewMethodName: String,
168 | mLayoutInflaterType: Int): String {
169 | val initView = StringBuilder()
170 | initView.append("// TODO:Copy method name to use\n")
171 | initView.append("private void $viewMethodName() {\n")
172 | if (mIsLayoutInflater) {
173 | // 添加LayoutInflater.from(this).inflate(R.layout.activity_main, null);
174 | val layoutInflater = "$mLayoutInflaterText = LayoutInflater.from($context).inflate(R.layout.$mSelectedText, null);\n"
175 | initView.append(layoutInflater)
176 | }
177 | mElements.filter(Element::isEnable).forEach {
178 | val inflater = if (mIsLayoutInflater) layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType) else ""
179 | initView.append("${it.fieldName}$inflater = ButterKnife.findById($mLayoutInflaterText, ${it.fullID});\n")
180 | }
181 | initView.append("}\n")
182 | return initView.toString()
183 | }
--------------------------------------------------------------------------------
/src/ListExt.kt:
--------------------------------------------------------------------------------
1 | import entitys.Element
2 | import java.util.*
3 |
4 | /**
5 | * FindViewById,创建OnClick方法和switch
6 | *
7 | * @param mOnClickList 可onclick的Element的集合
8 | *
9 | * @return String
10 | */
11 | fun ArrayList.createFindViewByIdOnClickMethodAndSwitch(): String {
12 | val onClick = StringBuilder()
13 | onClick.append("@Override public void onClick(View v) {\n")
14 | onClick.append("switch (v.getId()) {\n")
15 | // add default statement
16 | onClick.append("\tdefault:\n")
17 | onClick.append("\t\tbreak;\n")
18 | this.filter(Element::isClickable).forEach {
19 | onClick.append("\tcase ${it.fullID}:\n")
20 | onClick.append("\t\tbreak;\n")
21 | }
22 | onClick.append("}\n")
23 | onClick.append("}\n")
24 | return onClick.toString()
25 | }
26 |
27 | /**
28 | * ButterKnife,创建OnClick方法和switch
29 | *
30 | * @param mOnClickList 可onclick的Element的集合
31 | *
32 | * @return String
33 | */
34 | fun ArrayList.createButterKnifeOnClickMethodAndSwitch(): String {
35 | val onClick = StringBuilder()
36 | onClick.append("@butterknife.OnClick(")
37 | if (this.size == 1) {
38 | onClick.append(this[0].fullID)
39 | } else {
40 | onClick.append("{")
41 | this.forEachIndexed { i, element ->
42 | if (i != 0) {
43 | onClick.append(", ")
44 | }
45 | onClick.append(element.fullID)
46 | }
47 | onClick.append("}")
48 | }
49 | onClick.append(")\n")
50 | onClick.append("public void onClick(View v) {\n")
51 | onClick.append("switch (v.getId()) {\n")
52 | // add default statement
53 | onClick.append("\tdefault:\n")
54 | onClick.append("\t\tbreak;\n")
55 | this.forEach {
56 | onClick.append("\tcase ${it.fullID}:\n")
57 | onClick.append("\t\tbreak;\n")
58 | }
59 | onClick.append("}\n")
60 | onClick.append("}\n")
61 | return onClick.toString()
62 | }
63 |
--------------------------------------------------------------------------------
/src/ProjectExt.kt:
--------------------------------------------------------------------------------
1 |
2 | import com.intellij.openapi.project.Project
3 | import com.intellij.psi.JavaPsiFacade
4 | import com.intellij.psi.PsiClass
5 | import com.intellij.psi.PsiFile
6 | import com.intellij.psi.search.EverythingGlobalScope
7 | import com.intellij.psi.search.FilenameIndex
8 | import com.intellij.psi.search.GlobalSearchScope
9 | import entitys.Element
10 |
11 | infix fun Project.findClassByProject(className: String) =
12 | JavaPsiFacade.getInstance(this).findClass(className, EverythingGlobalScope(this))
13 |
14 | /**
15 | * 判断mClass是不是继承activityClass或者activityCompatClass
16 | * @param mProject mProject
17 | *
18 | * @param mClass mClass
19 | *
20 | * @return boolean
21 | */
22 | infix fun Project.isExtendsActivityOrActivityCompat(mClass: PsiClass): Boolean {
23 | // 根据类名查找类
24 | val activityClass = this findClassByProject "android.app.Activity"
25 | val activityCompatClass = this findClassByProject "android.support.v7.app.AppCompatActivity"
26 | return activityClass != null && mClass checkInheritor activityClass
27 | || activityCompatClass != null && mClass checkInheritor activityCompatClass
28 | }
29 |
30 | /**
31 | * 判断mClass是不是继承fragmentClass或者fragmentV4Class
32 | * @param mProject mProject
33 | *
34 | * @param mClass mClass
35 | *
36 | * @return boolean
37 | */
38 | infix fun Project.isExtendsFragmentOrFragmentV4(mClass: PsiClass): Boolean {
39 | // 根据类名查找类
40 | val fragmentClass = this findClassByProject "android.app.Fragment"
41 | val fragmentV4Class = this findClassByProject "android.support.v4.app.Fragment"
42 | return fragmentClass != null && mClass checkInheritor fragmentClass
43 | || fragmentV4Class != null && mClass checkInheritor fragmentV4Class
44 | }
45 | /**
46 | * FindViewById,获取xml里面的text
47 | *
48 | * @param element Element
49 | *
50 | * @param mProject Project
51 | *
52 | * @param psiFile PsiFile
53 | *
54 | * @return String
55 | */
56 | fun Project.createFieldText(element: Element, psiFile: PsiFile): String? {
57 | // 如果是text为空,则获取hint里面的内容
58 | var text: String? = element.xml.getAttributeValue("android:text") ?: element.xml.getAttributeValue("android:hint")
59 | text?.let {
60 | if (it.contains("@string/")) {
61 | text = it.replace("@string/".toRegex(), "")
62 | // 获取strings.xml
63 | val psiFiles = FilenameIndex.getFilesByName(this, "strings.xml", GlobalSearchScope.allScope(this))
64 | if (psiFiles.isNotEmpty()) {
65 | psiFiles
66 | .filter {
67 | // 获取src\main\res\values下面的strings.xml文件
68 | it.parent != null && it.parent!!.toString().contains("src\\main\\res\\values", false)
69 | }
70 | .filter {
71 | it.parent!!.toString().outInfo()
72 | val psiFilePath = psiFile.parent?.toString()!!
73 | val modulePath = it.parent?.toString()!!
74 | psiFilePath.substring(0, psiFilePath.indexOf("\\main\\")) == modulePath.substring(0, modulePath.indexOf("\\main\\"))
75 | }
76 | .forEach { text = it.getTextFromStringsXml(text!!) }
77 | }
78 | }
79 | }
80 | return text
81 | }
--------------------------------------------------------------------------------
/src/PsiClass.kt:
--------------------------------------------------------------------------------
1 | import com.intellij.psi.*
2 | import constant.Constant
3 | import org.apache.commons.lang.StringUtils
4 | import java.util.*
5 |
6 | /**
7 | * 获取initView方法里面的每条数据
8 |
9 | * @param mClass mClass
10 | *
11 | * @return PsiStatement[]
12 | */
13 | fun PsiClass.getInitViewBodyStatements(): Array? {
14 | // 获取initView方法
15 | val method = this.findMethodsByName(Constant.Ext.CREATOR_INITVIEW_NAME, false)
16 | return if (method.isNotEmpty() && method[0].body != null)
17 | method[0].body?.statements
18 | else null
19 | }
20 |
21 | /**
22 | * 获取onClick方法里面的每条数据
23 |
24 | * @param mClass mClass
25 | *
26 | * @return PsiElement[]
27 | */
28 | fun PsiClass.getOnClickStatement(): Array? {
29 | // 获取onClick方法
30 | val onClickMethods = this.findMethodsByName(Constant.FIELD_ONCLICK, false)
31 | return if (onClickMethods.isNotEmpty() && onClickMethods[0].body != null)
32 | onClickMethods[0].body?.children
33 | else null
34 | }
35 | /**
36 | * 获取包含@OnClick注解的方法
37 |
38 | * @param mClass mClass
39 | *
40 | * @return PsiMethod
41 | */
42 | fun PsiClass.getPsiMethodByButterKnifeOnClick(): PsiMethod? {
43 | for (psiMethod in this.methods) {
44 | // 获取方法的注解
45 | val modifierList = psiMethod.modifierList
46 | val annotations = modifierList.annotations
47 | annotations
48 | .map { it.qualifiedName }
49 | .filter { it != null && it == "butterknife.OnClick" }
50 | .forEach {
51 | // 包含@OnClick注解
52 | return psiMethod
53 | }
54 | }
55 | return null
56 | }
57 |
58 | /**
59 | * 获取View类型的变量名
60 |
61 | * @param mClass mClass
62 | *
63 | * @return String
64 | */
65 | fun PsiClass.getPsiMethodParamsViewField(): String? {
66 | val butterKnifeOnClickMethod = this.getPsiMethodByButterKnifeOnClick()
67 | if (butterKnifeOnClickMethod != null) {
68 | // 获取方法的指定参数类型的变量名
69 | val parameterList = butterKnifeOnClickMethod.parameterList
70 | val parameters = parameterList.parameters
71 | parameters
72 | .filter { it.typeElement != null && it.typeElement?.text == "View" }
73 | .forEach { return it.name }
74 | }
75 | return null
76 | }
77 |
78 | /**
79 | * 获取包含@OnClick注解里面的值
80 | *
81 | * @return List
82 | */
83 | fun PsiClass.getPsiMethodByButterKnifeOnClickValue(): ArrayList {
84 | val onClickValue = ArrayList()
85 | this.getPsiMethodByButterKnifeOnClick()?.let {
86 | // 获取方法的注解
87 | val modifierList = it.modifierList
88 | modifierList.annotations.filter {
89 | it.qualifiedName != null && it.qualifiedName == "butterknife.OnClick"
90 | }.forEach {
91 | val text = it.text.replace("(", "")
92 | .replace(")", "")
93 | .replace("{", "")
94 | .replace("}", "")
95 | .replace(" ", "")
96 | .replace("@OnClick", "")
97 | if (!StringUtils.isEmpty(text)) {
98 | val split = text.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray().dropLastWhile(String::isEmpty).toTypedArray()
99 | split.filterNotTo(onClickValue) { StringUtils.isEmpty(it) }
100 | }
101 | return@let
102 | }
103 | }
104 | return onClickValue
105 | }
106 |
107 | /**
108 | * 添加注解到方法
109 |
110 | * @param mClass mClass
111 | *
112 | * @param mFactory mFactory
113 | *
114 | * @param onClickValue onClickValue
115 | */
116 | fun PsiClass.createOnClickAnnotation(mFactory: PsiElementFactory, onClickValue: ArrayList) {
117 | this.getPsiMethodByButterKnifeOnClick()?.let {
118 | // 获取方法的注解
119 | val modifierList = it.modifierList
120 | modifierList.annotations.filter {
121 | it.qualifiedName != null && it.qualifiedName == "butterknife.OnClick"
122 | }.forEach {
123 | val annotationText = StringBuilder()
124 | annotationText.append("@OnClick(")
125 | if (onClickValue.size == 1) {
126 | annotationText.append(onClickValue[0])
127 | } else {
128 | annotationText.append("{")
129 | onClickValue.forEachIndexed { index, value ->
130 | if (index != 0) {
131 | annotationText.append(", ")
132 | }
133 | annotationText.append(value)
134 | }
135 | annotationText.append("}")
136 | }
137 | annotationText.append(")")
138 | modifierList.addBefore(mFactory.createAnnotationFromText(annotationText.toString(), modifierList), it)
139 | it.delete()
140 | return@let
141 | }
142 | }
143 | }
144 | /**
145 | * 判断是否存在ButterKnife.bind(this)/ButterKnife.bind(this, view)
146 | * @param mClass mClass
147 | *
148 | * @return boolean
149 | */
150 | fun PsiClass.isButterKnifeBindExist(): Boolean {
151 | val onCreateMethod = this.getPsiMethodByName(Constant.PSI_METHOD_BY_ONCREATE)
152 | val onCreateViewMethod = this.getPsiMethodByName(Constant.PSI_METHOD_BY_ONCREATEVIEW)
153 | return !(onCreateMethod != null && onCreateMethod.body != null
154 | && onCreateMethod.body!!.text.contains(Constant.Ext.FIELD_BUTTERKNIFE_BIND)
155 | || onCreateViewMethod != null && onCreateViewMethod.body != null
156 | && onCreateViewMethod.body!!.text.contains(Constant.Ext.FIELD_BUTTERKNIFE_BIND))
157 | }
158 |
159 |
160 | /**
161 | * 根据方法名获取方法
162 | *
163 | * @param mClass mClass
164 | *
165 | * @param methodName methodName
166 | *
167 | * @return PsiMethod
168 | */
169 | fun PsiClass.getPsiMethodByName(methodName: String): PsiMethod? =
170 | this.methods.firstOrNull { it.name == methodName }
171 |
--------------------------------------------------------------------------------
/src/PsiFileExt.kt:
--------------------------------------------------------------------------------
1 | import com.intellij.psi.PsiClass
2 | import com.intellij.psi.PsiElement
3 | import com.intellij.psi.PsiFile
4 | import com.intellij.psi.XmlRecursiveElementVisitor
5 | import com.intellij.psi.search.FilenameIndex
6 | import com.intellij.psi.search.GlobalSearchScope
7 | import com.intellij.psi.xml.XmlFile
8 | import com.intellij.psi.xml.XmlTag
9 | import constant.Constant
10 | import entitys.Element
11 | import org.apache.commons.lang.StringUtils
12 | import java.util.*
13 | import java.util.regex.Pattern
14 |
15 | fun PsiFile.fileAccept(accept: (element: PsiElement) -> Unit) =
16 | this.accept(object : XmlRecursiveElementVisitor() {
17 | override fun visitElement(element: PsiElement) {
18 | super.visitElement(element)
19 | accept.invoke(element)
20 | }
21 | })
22 |
23 | /**
24 | * 获取所有id
25 | * @param file file
26 | *
27 | * @param elements elements
28 | */
29 | fun getIDsFromLayoutToList(psiFile: PsiFile, elements: ArrayList) {
30 | psiFile.fileAccept { element ->
31 | // 解析Xml标签
32 | if (element is XmlTag) {
33 | with(element) {
34 | // 获取Tag的名字(TextView)或者自定义
35 | val name: String = name
36 | // 如果有include
37 | if (name.equals("include", ignoreCase = true)) {
38 | // 获取布局
39 | val layout = getAttribute("layout", null) ?: return@fileAccept
40 | val layoutName = layout.value.getLayoutName() ?: return@fileAccept
41 | // 获取project
42 | val project = this.project
43 | // 布局文件
44 | var include: XmlFile? = null
45 | val psiFiles = FilenameIndex.getFilesByName(project, layoutName + Constant.SELECTED_TEXT_SUFFIX, GlobalSearchScope.allScope(project))
46 | if (psiFiles.isNotEmpty()) {
47 | include = if (psiFiles.size > 1) {
48 | val psiFilePath = psiFile.parent?.toString()!!
49 | val psiFiles1 = psiFiles.filter {
50 | val modulePath = it.parent?.toString()!!
51 | modulePath.contains("\\src\\main\\res\\layout") && psiFilePath.substring(0, psiFilePath.indexOf("\\main\\")) == modulePath.substring(0, modulePath.indexOf("\\main\\"))
52 | }
53 | if (psiFiles1.isEmpty()) return@fileAccept else psiFiles1[0] as XmlFile
54 | } else {
55 | psiFiles[0] as XmlFile
56 | }
57 | }
58 | if (include != null) {
59 | // 递归
60 | getIDsFromLayoutToList(include, elements)
61 | return@fileAccept
62 | }
63 | }
64 | // 获取id字段属性
65 | val id = getAttribute("android:id", null) ?: return@fileAccept
66 | // 获取id的值
67 | val idValue = id.value ?: return@fileAccept
68 | // 获取clickable
69 | val clickableAttr = getAttribute("android:clickable", null)
70 | var clickable = false
71 | if (clickableAttr != null && !StringUtils.isEmpty(clickableAttr.value)) {
72 | clickable = clickableAttr.value == "true"
73 | }
74 | if (name == "Button") {
75 | clickable = true
76 | }
77 | // 添加到list
78 | try {
79 | val e = Element(name, idValue, clickable, this)
80 | elements.add(e)
81 | } catch (ignored: IllegalArgumentException) {
82 | ignored.printStackTrace()
83 | }
84 | }
85 | }
86 | }
87 | }
88 |
89 | /**
90 | * 解析xml获取string的值
91 |
92 | * @param psiFile psiFile
93 | *
94 | * @param text text
95 | *
96 | * @return String
97 | */
98 | fun PsiFile.getTextFromStringsXml(text: String): String? {
99 | var stringValue: String? = null
100 | this.fileAccept { element ->
101 | if (element is XmlTag) {
102 | with(element) {
103 | if (name == "string" && getAttributeValue("name") == text) {
104 | val value = StringBuilder()
105 | children.forEach {
106 | value.append(it.text)
107 | }
108 | // value = My Application
109 | // 用正则获取值
110 | val p = Pattern.compile("(.*)")
111 | val m = p.matcher(value.toString())
112 | while (m.find()) {
113 | stringValue = m.group(1)
114 | }
115 | }
116 | }
117 | }
118 | }
119 | return stringValue
120 | }
121 |
122 | infix fun PsiClass.checkInheritor(toClass: PsiClass) = this.isInheritor(toClass, true)
123 |
--------------------------------------------------------------------------------
/src/StringExt.kt:
--------------------------------------------------------------------------------
1 |
2 | import com.intellij.notification.*
3 | import constant.Constant
4 | import entitys.Element
5 | import org.apache.commons.lang.StringUtils
6 | import java.util.*
7 |
8 |
9 | /**
10 | * 第一个字母大写
11 | *
12 | * @param key key
13 | *
14 | * @return String
15 | */
16 | fun String.firstToUpperCase(): String = this.substring(0, 1).toUpperCase(Locale.CHINA) + this.substring(1)
17 |
18 | /**
19 | * 输出到Log窗口
20 | */
21 | fun String.outInfo() {
22 | NotificationsConfiguration.getNotificationsConfiguration().register(Constant.GENERATEFINDVIEWBYID, NotificationDisplayType.NONE)
23 | Notifications.Bus.notify(
24 | Notification(Constant.GENERATEFINDVIEWBYID, "${Constant.GENERATEFINDVIEWBYID} [INFO]", this, NotificationType.INFORMATION))
25 | }
26 |
27 | /**
28 | * 根据layoutInflaterType生成不同内容
29 | *
30 | * @param mLayoutInflaterText mLayoutInflaterText
31 | *
32 | * @param mLayoutInflaterType mLayoutInflaterType
33 | *
34 | * @return String
35 | */
36 | fun layoutInflaterType2Str(mLayoutInflaterText: String, mLayoutInflaterType: Int): String = when (mLayoutInflaterType) {
37 | 1 -> "_$mLayoutInflaterText"
38 | 2 -> mLayoutInflaterText.firstToUpperCase()
39 | else -> mLayoutInflaterText.substring(1)
40 | }
41 |
42 | /**
43 | * layout.getValue()返回的值为@layout/layout_view
44 | * @param layout layout
45 | *
46 | * @return String
47 | */
48 | fun String?.getLayoutName(): String? {
49 | if (this == null || !this.startsWith("@") || !this.contains("/")) {
50 | return null
51 | }
52 |
53 | // @layout layout_view
54 | val parts = this.split("/".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
55 | if (parts.size != 2) {
56 | return null
57 | }
58 | // layout_view
59 | return parts[1]
60 | }
61 |
62 | /**
63 | * 驼峰
64 | *
65 | * @param fieldNames fieldName
66 | *
67 | * @param type type
68 | *
69 | * @return String
70 | */
71 | infix fun String.getFieldName(type: Int): String {
72 | var fieldName = this
73 | if (!StringUtils.isEmpty(fieldName)) {
74 | val names = fieldName.split("_".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
75 | when (type) {
76 | 2 -> {
77 | // aaBbCc
78 | val sb = StringBuilder()
79 | for (i in names.indices) {
80 | if (i == 0) {
81 | sb.append(names[i])
82 | } else {
83 | sb.append(names[i].firstToUpperCase())
84 | }
85 | }
86 | sb.append("View")
87 | fieldName = sb.toString()
88 | }
89 | 3 -> {
90 | // mAaBbCc
91 | val sb = StringBuilder()
92 | for (i in names.indices) {
93 | if (i == 0) {
94 | sb.append("m")
95 | }
96 | sb.append(names[i].firstToUpperCase())
97 | }
98 | sb.append("View")
99 | fieldName = sb.toString()
100 | }
101 | else -> fieldName += "_view"
102 | }
103 | }
104 | return fieldName
105 | }
106 |
107 | /**
108 | * 创建onCreate方法(Fragment)
109 | *
110 | * @param mSelectedText mSelectedText
111 | *
112 | * @return String
113 | */
114 | fun String.createFragmentOnCreateMethod(): String = "@Override public void onCreate(@Nullable android.os.Bundle savedInstanceState) {\n" +
115 | "super.onCreate(savedInstanceState);\n" +
116 | "\tview = View.inflate(getActivity(), R.layout.$this, null);\n" +
117 | "}"
118 |
119 | /**
120 | * FindViewById,创建字段
121 | *
122 | * @param text 注释内容
123 | *
124 | * @param element Element
125 | *
126 | * @param mIsLayoutInflater 是否选中LayoutInflater
127 | *
128 | * @param mLayoutInflaterText 选中的布局的变量名
129 | *
130 | * @param mLayoutInflaterType mLayoutInflaterType
131 | *
132 | * @return String
133 | */
134 | fun String?.createFieldByElement(element: Element, mIsLayoutInflater: Boolean, mLayoutInflaterText: String, mLayoutInflaterType: Int): String {
135 | val fromText = StringBuilder()
136 | this?.let {
137 | fromText.append("/** $it */\n")
138 | }
139 | with(element) {
140 | fromText.append("private $name $fieldName")
141 | }
142 | if (mIsLayoutInflater) {
143 | fromText.append(layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType))
144 | }
145 | fromText.append(";")
146 | return fromText.toString()
147 | }
148 |
149 | /**
150 | * ButterKnife,在OnClick方法里面创建switch
151 | *
152 | * @param psiMethodParamsViewField View类型的变量名
153 | *
154 | * @param onClickValues 注解里面跟OnClickList的id集合
155 | *
156 | * @return String
157 | */
158 | fun String.createSwitchByOnClickMethod(onClickValues: ArrayList): String {
159 | val psiSwitch = StringBuilder()
160 | psiSwitch.append("switch ($this.getId()) {\n")
161 | // add default statement
162 | psiSwitch.append("\tdefault:\n")
163 | psiSwitch.append("\t\tbreak;\n")
164 | onClickValues.forEach {
165 | psiSwitch.append("\tcase $it:\n")
166 | psiSwitch.append("\t\tbreak;\n")
167 | }
168 | psiSwitch.append("}")
169 | return psiSwitch.toString()
170 | }
171 |
172 | /**
173 | * FindViewById,创建ViewHolder
174 | *
175 | * @param viewHolderName viewHolderName
176 | *
177 | * @param viewHolderRootView viewHolderRootView
178 | *
179 | * @param mElements mElements
180 | *
181 | * @param mNeedCasts 是否需要强转
182 | *
183 | * @return String
184 | */
185 | fun String.createFindViewByIdViewHolder(viewHolderRootView: String,
186 | mElements: ArrayList,
187 | mNeedCasts: Boolean): String {
188 | // ViewHolder
189 | val viewHolderText = StringBuilder()
190 | // ViewHolder的Constructor
191 | val viewHolderConstructorText = StringBuilder()
192 | // rootView
193 | viewHolderText.append("android.view.View $viewHolderRootView;\n")
194 | // Constructor
195 | viewHolderConstructorText.append("$this(android.view.View ) {\nthis.$viewHolderRootView = $viewHolderRootView;\n")
196 | // 添加field和findViewById
197 | mElements.forEach {
198 | with(it) {
199 | // 添加Field
200 | viewHolderText.append("$name $fieldName;\n")
201 | // 添加findViewById
202 | viewHolderConstructorText.append("this.$fieldName = ")
203 | if (mNeedCasts) {
204 | viewHolderConstructorText.append("($name) ")
205 | }
206 | viewHolderConstructorText.append("$viewHolderRootView.findViewById($fullID);\n")
207 | }
208 | }
209 | viewHolderConstructorText.append("}")
210 | // 添加Constructor到ViewHolder
211 | viewHolderText.append(viewHolderConstructorText.toString())
212 | return viewHolderText.toString()
213 | }
214 |
215 | /**
216 | * ButterKnife,创建ViewHolder
217 | *
218 | * @param viewHolderName viewHolderName
219 | *
220 | * @param viewHolderRootView viewHolderRootView
221 | *
222 | * @param mElements mElements
223 | *
224 | * @return String
225 | */
226 | fun String.createButterKnifeViewHolder(viewHolderRootView: String, mElements: ArrayList): String {
227 | // ViewHolder
228 | val viewHolderText = StringBuilder()
229 | // ViewHolder的Constructor
230 | val viewHolderConstructorText = StringBuilder()
231 | // 添加field和findViewById
232 | mElements.forEach {
233 | // 添加Field
234 | viewHolderText.append("@BindView(${it.fullID})\n${it.name} ${it.fieldName};\n")
235 | }
236 | // Constructor
237 | viewHolderConstructorText.append("$this(android.view.View $viewHolderRootView) {\nButterKnife.bind(this, $viewHolderRootView);\n}")
238 | // 添加Constructor到ViewHolder
239 | viewHolderText.append(viewHolderConstructorText.toString())
240 | return viewHolderText.toString()
241 | }
242 | /**
243 | * 创建onCreate方法
244 | *
245 | * @param mSelectedText mSelectedText
246 | *
247 | * @param mIsButterKnife mIsButterKnife
248 | *
249 | * @return String
250 | */
251 | fun String.createOnCreateMethod(mIsButterKnife: Boolean): String {
252 | val method = StringBuilder()
253 | method.append("@Override protected void onCreate(android.os.Bundle savedInstanceState) {\n")
254 | method.append("super.onCreate(savedInstanceState);\n")
255 | method.append("\t// TODO:OnCreate Method has been created, run ")
256 | method.append(if(!mIsButterKnife) "FindViewById" else "ButterKnife")
257 | method.append(" again to generate code\n\tsetContentView(R.layout.$this);\n")
258 | method.append(if(!mIsButterKnife) "\t\tinitView();\n" else "\t\tButterKnife.bind(this);\n")
259 | method.append("}")
260 | return method.toString()
261 | }
--------------------------------------------------------------------------------
/src/actions/ButterKnifeAction.kt:
--------------------------------------------------------------------------------
1 | package actions
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.actionSystem.PlatformDataKeys
6 | import com.intellij.openapi.ui.Messages
7 | import com.intellij.psi.search.FilenameIndex
8 | import com.intellij.psi.search.GlobalSearchScope
9 | import com.intellij.psi.util.PsiUtilBase
10 | import com.intellij.psi.xml.XmlFile
11 | import constant.Constant
12 | import entitys.Element
13 | import getIDsFromLayoutToList
14 | import getTargetClass
15 | import isExtendsActivityOrActivityCompat
16 | import isExtendsFragmentOrFragmentV4
17 | import showPopupBalloon
18 | import utils.CreateMethodCreator
19 | import views.ButterKnifeDialog
20 | import java.util.*
21 |
22 | /**
23 | * @author Jowan
24 | */
25 | class ButterKnifeAction : AnAction() {
26 | private var mDialog: ButterKnifeDialog? = null
27 |
28 | override fun actionPerformed(e: AnActionEvent) {
29 | // 获取project
30 | val project = e.project ?: return
31 | // 获取选中内容
32 | val mEditor = e.getData(PlatformDataKeys.EDITOR) ?: return
33 | // 未选中布局内容,显示dialog
34 | val popupTime = 5
35 | val mSelectedText: String? = mEditor.selectionModel.selectedText ?: let {
36 | Messages.showInputDialog(project,
37 | Constant.Action.SELECTED_MESSAGE,
38 | Constant.Action.SELECTED_TITLE,
39 | Messages.getInformationIcon()) ?: let {
40 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_NAME, popupTime)
41 | return
42 | }
43 | }
44 | // 判断是否有onCreate/onCreateView方法
45 | val psiFile = PsiUtilBase.getPsiFileInEditor(mEditor, project) ?: return
46 | val psiClass = mEditor getTargetClass psiFile ?: let {
47 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_POINT, popupTime)
48 | return
49 | }
50 | // 判断是否有onCreate方法
51 | if (project isExtendsActivityOrActivityCompat psiClass && psiClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
52 | // 写onCreate方法
53 | mSelectedText?.let {
54 | CreateMethodCreator(mEditor, psiFile, psiClass, Constant.CREATOR_COMMAND_NAME,
55 | it, Constant.CLASS_TYPE_BY_ACTIVITY, true).execute()
56 | }
57 | return
58 | }
59 | // 判断是否有onCreateView方法
60 | if (project isExtendsFragmentOrFragmentV4 psiClass && psiClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false).isEmpty()) {
61 | mSelectedText?.let {
62 | CreateMethodCreator(mEditor, psiFile, psiClass, Constant.CREATOR_COMMAND_NAME,
63 | it, Constant.CLASS_TYPE_BY_FRAGMENT, true).execute()
64 | }
65 | return
66 | }
67 | // 获取布局文件,通过FilenameIndex.getFilesByName获取GlobalSearchScope.allScope(project)搜索整个项目
68 | val psiFiles = FilenameIndex.getFilesByName(project,
69 | mSelectedText + Constant.SELECTED_TEXT_SUFFIX,
70 | GlobalSearchScope.allScope(project))
71 | if (psiFiles.isEmpty()) {
72 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_SELECTED, popupTime)
73 | return
74 | }
75 | val xmlFile = if (psiFiles.size > 1) {
76 | val psiFilePath = psiFile.parent?.toString()!!
77 | val psiFiles1 = psiFiles.filter {
78 | val modulePath = it.parent?.toString()!!
79 | modulePath.contains("\\src\\main\\res\\layout") && psiFilePath.substring(0, psiFilePath.indexOf("\\main\\")) == modulePath.substring(0, modulePath.indexOf("\\main\\"))
80 | }
81 | if (psiFiles1.isEmpty()) {
82 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_SELECTED, popupTime)
83 | return
84 | } else psiFiles1[0] as XmlFile
85 | } else {
86 | psiFiles[0] as XmlFile
87 | }
88 | // val xmlFile = psiFiles[0] as XmlFile
89 | val elements = ArrayList()
90 | getIDsFromLayoutToList(xmlFile, elements)
91 | // 将代码写入文件,不允许在主线程中进行实时的文件写入
92 | if (elements.size == 0) {
93 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_ID, popupTime)
94 | return
95 | }
96 | // 有的话就创建变量和ButterKnife代码
97 | if (mDialog != null && mDialog!!.isShowing) {
98 | mDialog?.cancelDialog()
99 | }
100 | mDialog = ButterKnifeDialog(mEditor = mEditor,
101 | mProject = project,
102 | mPsiFile = psiFile,
103 | mClass = psiClass,
104 | mElements = elements,
105 | mSelectedText = mSelectedText!!,
106 | mIsButterKnife = true,
107 | elementSize = elements.size)
108 | mDialog?.showDialog()
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/actions/FindViewByIdAction.kt:
--------------------------------------------------------------------------------
1 | package actions
2 |
3 | import com.intellij.openapi.actionSystem.AnAction
4 | import com.intellij.openapi.actionSystem.AnActionEvent
5 | import com.intellij.openapi.actionSystem.PlatformDataKeys
6 | import com.intellij.openapi.ui.Messages
7 | import com.intellij.psi.search.FilenameIndex
8 | import com.intellij.psi.search.GlobalSearchScope
9 | import com.intellij.psi.util.PsiUtilBase
10 | import com.intellij.psi.xml.XmlFile
11 | import constant.Constant
12 | import entitys.Element
13 | import getIDsFromLayoutToList
14 | import getTargetClass
15 | import isExtendsActivityOrActivityCompat
16 | import isExtendsFragmentOrFragmentV4
17 | import showPopupBalloon
18 | import utils.CreateMethodCreator
19 | import views.FindViewByIdDialog
20 | import java.util.*
21 |
22 | /**
23 | * @author Jowan
24 | */
25 | class FindViewByIdAction : AnAction() {
26 | private var mDialog: FindViewByIdDialog? = null
27 |
28 | override fun actionPerformed(e: AnActionEvent) {
29 | // 获取project
30 | val project = e.project ?: return
31 | // 获取选中内容
32 | val mEditor = e.getData(PlatformDataKeys.EDITOR) ?: return
33 | // 未选中布局内容,显示dialog
34 | val popupTime = 5
35 | val mSelectedText: String? = mEditor.selectionModel.selectedText ?: let {
36 | Messages.showInputDialog(project,
37 | Constant.Action.SELECTED_MESSAGE,
38 | Constant.Action.SELECTED_TITLE,
39 | Messages.getInformationIcon()) ?: let {
40 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_NAME, popupTime)
41 | return
42 | }
43 | }
44 | // 判断是否有onCreate/onCreateView方法
45 | val psiFile = PsiUtilBase.getPsiFileInEditor(mEditor, project) ?: return
46 | val psiClass = mEditor getTargetClass psiFile ?: let {
47 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_POINT, popupTime)
48 | return
49 | }
50 | // 判断是否有onCreate方法
51 | if (project isExtendsActivityOrActivityCompat psiClass && psiClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
52 | // 写onCreate方法
53 | mSelectedText?.let {
54 | CreateMethodCreator(mEditor, psiFile, psiClass, Constant.CREATOR_COMMAND_NAME,
55 | it, Constant.CLASS_TYPE_BY_ACTIVITY, false).execute()
56 | }
57 | return
58 | }
59 | // 判断是否有onCreateView方法
60 | if (project isExtendsFragmentOrFragmentV4 psiClass && psiClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false).isEmpty()) {
61 | mSelectedText?.let {
62 | CreateMethodCreator(mEditor, psiFile, psiClass, Constant.CREATOR_COMMAND_NAME,
63 | it, Constant.CLASS_TYPE_BY_FRAGMENT, false).execute()
64 | }
65 | return
66 | }
67 | // 获取布局文件,通过FilenameIndex.getFilesByName获取GlobalSearchScope.allScope(project)搜索整个项目
68 | val psiFiles = FilenameIndex.getFilesByName(project,
69 | mSelectedText + Constant.SELECTED_TEXT_SUFFIX,
70 | GlobalSearchScope.allScope(project))
71 | if (psiFiles.isEmpty()) {
72 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_SELECTED, popupTime)
73 | return
74 | }
75 | val xmlFile = if (psiFiles.size > 1) {
76 | val psiFilePath = psiFile.parent?.toString()!!
77 | val psiFiles1 = psiFiles.filter {
78 | val modulePath = it.parent?.toString()!!
79 | modulePath.contains("\\src\\main\\res\\layout") && psiFilePath.substring(0, psiFilePath.indexOf("\\main\\")) == modulePath.substring(0, modulePath.indexOf("\\main\\"))
80 | }
81 | if (psiFiles1.isEmpty()) {
82 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_SELECTED, popupTime)
83 | return
84 | } else psiFiles1[0] as XmlFile
85 | } else {
86 | psiFiles[0] as XmlFile
87 | }
88 | val elements = ArrayList()
89 | getIDsFromLayoutToList(xmlFile, elements)
90 | // 将代码写入文件,不允许在主线程中进行实时的文件写入
91 | if (elements.size == 0) {
92 | mEditor.showPopupBalloon(Constant.Action.SELECTED_ERROR_NO_ID, popupTime)
93 | return
94 | }
95 | // 有的话就创建变量和findViewById
96 | if (mDialog != null && mDialog!!.isShowing) {
97 | mDialog?.cancelDialog()
98 | }
99 | mDialog = FindViewByIdDialog(mEditor = mEditor,
100 | mProject = project,
101 | mPsiFile = psiFile,
102 | mClass = psiClass,
103 | mElements = elements,
104 | mSelectedText = mSelectedText!!,
105 | mIsButterKnife = false,
106 | elementSize = elements.size)
107 | mDialog?.showDialog()
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/constant/Constant.kt:
--------------------------------------------------------------------------------
1 | package constant
2 |
3 | /**
4 | * @author Jowan
5 | */
6 | object Constant {
7 | const val GENERATEFINDVIEWBYID = "GenerateFindViewById"
8 | const val ACTION_FINDVIEWBYID = "FindViewById"
9 | const val ACTION_BUTTERKNIFE = "ButterKnife"
10 | const val SELECTED_TEXT_SUFFIX = ".xml"
11 | const val PSI_METHOD_BY_ONCREATE = "onCreate"
12 | const val PSI_METHOD_BY_ONCREATEVIEW = "onCreateView"
13 | const val CREATOR_COMMAND_NAME = "Generate Injections"
14 | const val CLASS_TYPE_BY_ACTIVITY = "activity"
15 | const val CLASS_TYPE_BY_FRAGMENT = "fragment"
16 | const val FIELD_ON_CLICK = "OnClick"
17 | const val FIELD_ONCLICK = "onClick"
18 |
19 | object Action {
20 | const val SELECTED_MESSAGE = "布局内容:(不需要输入R.layout.)"
21 | const val SELECTED_TITLE = "未选中布局内容,请输入layout文件名"
22 | const val SELECTED_ERROR_NO_NAME = "未输入layout文件名"
23 | const val SELECTED_ERROR_NO_SELECTED = "未找到选中的布局文件"
24 | const val SELECTED_ERROR_NO_ID = "未找到任何Id"
25 | const val SELECTED_ERROR_NO_POINT = "光标未在Class内"
26 | const val SELECTED_LAYOUT_FIELD_TEXT_NO_NULL = "LayoutInflater变量名不能为空"
27 | const val SELECTED_SUCCESS = "生成成功"
28 | }
29 |
30 | object Dialog {
31 | const val TITLE_BUTTERKNIFE = "ButterKnifeDialog"
32 | const val TITLE_FINDVIEWBYID = "FindViewByIdDialog"
33 | const val TABLE_FIELD_VIEW_WIDGET = "ViewWidget"
34 | const val TABLE_FIELD_VIEW_ID = "ViewId"
35 | const val TABLE_FIELD_VIEW_FILED = "ViewFiled"
36 | const val FIELD_LAYOUT_INFLATER = "LayoutInflater.from(context).inflater"
37 | const val FIELD_CHECK_ALL = "CheckAll"
38 | const val VIEWHOLDER_CHECK = "Create ViewHolder"
39 | const val NEED_CASTS = "Forced Casts(SupportLibrary uncheck the above version of 26)"
40 | const val FIELD_BUTTERKNIFE_BIND = "ButterKnife.bind()"
41 | const val BUTTON_CONFIRM = "确定"
42 | const val BUTTON_CANCEL = "取消"
43 | }
44 |
45 | object Ext {
46 | const val CREATOR_NO_ONCREATE_METHOD = "没有OnCreate方法,已创建OnCreate方法,请重新使用"
47 | const val CREATOR_NO_ONCREATEVIEW_METHOD = "没有OnCreateView方法,已创建OnCreateView方法,请重新使用"
48 | const val CREATOR_INITVIEW_NAME = "initView"
49 | const val CREATOR_VIEW_NAME = "view"
50 | const val CREATOR_UNBINDER_NAME = "unbinder"
51 | const val CREATOR_SETCONTENTVIEW_METHOD = "setContentView"
52 | const val CREATOR_ONDESTROYVIEW_METHOD = "onDestroyView"
53 | const val CREATOR_UNBINDER_FIELD = "unbinder.unbind();"
54 | const val FIELD_BUTTERKNIFE_BIND = "ButterKnife.bind"
55 | const val UNKNOWN_ERROR = "Unknown Error"
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/entitys/Element.kt:
--------------------------------------------------------------------------------
1 | package entitys
2 |
3 | import com.intellij.psi.xml.XmlTag
4 | import firstToUpperCase
5 | import org.apache.commons.lang.StringUtils
6 | import java.util.regex.Pattern
7 |
8 | /**
9 | * @author Jowan
10 | */
11 | class Element
12 | /**
13 | * 构造函数
14 | * @param name View的名字
15 | *
16 | * @param id android:id属性
17 | *
18 | * @param clickable clickable
19 | *
20 | */
21 | (name: String, id: String, clickable: Boolean, var xml: XmlTag) {
22 | // id
23 | var id: String? = null
24 | // 名字如TextView
25 | var name: String? = null
26 | // 命名1 aa_bb_cc; 2 aaBbCc 3 mAaBbCc
27 | var fieldNameType = 3
28 | /**
29 | * 获取变量名
30 | * @return 变量名
31 | */
32 | // aaBbCc mAaBbCc
33 | var fieldName: String = ""
34 | get() {
35 | if (StringUtils.isEmpty(field)) {
36 | var fieldName: String = id!!
37 | val names = id!!.split("_".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
38 | when (fieldNameType) {
39 | 2 -> {
40 | val sb = StringBuilder()
41 | for (i in names.indices) {
42 | if (i == 0) {
43 | sb.append(names[i])
44 | } else {
45 | sb.append(names[i].firstToUpperCase())
46 | }
47 | }
48 | fieldName = sb.toString()
49 | }
50 | 3 -> {
51 | val sb = StringBuilder()
52 | for (i in names.indices) {
53 | if (i == 0) {
54 | sb.append("m")
55 | }
56 | sb.append(names[i].firstToUpperCase())
57 | }
58 | fieldName = sb.toString()
59 | }
60 | 1 -> fieldName = id!!
61 | }
62 | this.fieldName = fieldName
63 | }
64 | return field
65 | }
66 | // 是否生成
67 | var isEnable = true
68 | // 是否有clickable
69 | var isClickEnable = false
70 | private set
71 | // 是否Clickable
72 | var isClickable = false
73 |
74 | init {
75 | // id
76 | val matcher = sIdPattern.matcher(id)
77 | if (matcher.find() && matcher.groupCount() > 1) {
78 | this.id = matcher.group(2)
79 | }
80 |
81 | if (this.id == null) {
82 | throw IllegalArgumentException("Invalid format of view id")
83 | }
84 |
85 | this.name = name
86 |
87 | val packages = name.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
88 | if (packages.size > 1) {
89 | // com.example.CustomView
90 | this.name = packages[packages.size - 1]
91 | }
92 |
93 | this.isClickEnable = true
94 |
95 | this.isClickable = clickable
96 | }
97 |
98 | fun setClickEnable() {
99 | this.isClickEnable = true
100 | }
101 |
102 | /**
103 | * 获取id,R.id.id
104 | * @return R.id.id
105 | */
106 | val fullID: String
107 | get() {
108 | val rPrefix = "R.id."
109 | return rPrefix + id!!
110 | }
111 |
112 | companion object {
113 |
114 | // 判断id正则
115 | private val sIdPattern = Pattern.compile("@\\+?(android:)?id/([^$]+)$", Pattern.CASE_INSENSITIVE)
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/entitys/IdBean.kt:
--------------------------------------------------------------------------------
1 | package entitys
2 |
3 | import java.awt.LayoutManager
4 | import java.awt.event.FocusEvent
5 | import java.awt.event.FocusListener
6 | import javax.swing.JCheckBox
7 | import javax.swing.JLabel
8 | import javax.swing.JPanel
9 | import javax.swing.JTextField
10 | import javax.swing.border.EmptyBorder
11 |
12 | /**
13 | * @author Jowan
14 | */
15 | class IdBean
16 | /**
17 | * 构造方法
18 | * @param layout 布局
19 | *
20 | * @param emptyBorder border
21 | *
22 | * @param jCheckBox 是否生成+name
23 | *
24 | * @param jLabelId id
25 | *
26 | * @param jCheckBoxClick onClick
27 | *
28 | * @param jTextField 字段名
29 | *
30 | * @param enable 是否生成
31 | *
32 | * @param clickable clickable
33 | *
34 | * @param clickEnable 是否Enable
35 | */
36 | (layout: LayoutManager,
37 | emptyBorder: EmptyBorder,
38 | private val mEnableCheckBox: JCheckBox,
39 | private val mIdJLabel: JLabel,
40 | private val mClickCheckBox: JCheckBox,
41 | private val mFieldJTextField: JTextField,
42 | enable: Boolean,
43 | clickable: Boolean,
44 | clickEnable: Boolean) : JPanel(layout) {
45 |
46 | /**
47 | * mEnableCheckBox接口
48 | */
49 | private var mEnableListener: ((enableCheckBox: JCheckBox) -> Unit)? = null
50 |
51 | fun setEnableActionListener(enableListener: (enableCheckBox: JCheckBox) -> Unit) {
52 | mEnableListener = enableListener
53 | }
54 |
55 | /**
56 | * mFieldJTextField接口
57 | */
58 | private var mFieldFocusListener: ((fieldJTextField: JTextField) -> Unit)? = null
59 |
60 | fun setFieldFocusListener(fieldFocusListener: (fieldJTextField: JTextField) -> Unit) {
61 | mFieldFocusListener = fieldFocusListener
62 | }
63 |
64 | /**
65 | * mClickCheckBox接口
66 | */
67 | private var mClickListener: ((clickCheckBox: JCheckBox) -> Unit)? = null
68 |
69 | fun setClickActionListener(clickListener: (clickCheckBox: JCheckBox) -> Unit) {
70 | mClickListener = clickListener
71 | }
72 |
73 | init {
74 | initLayout(layout, emptyBorder)
75 | initComponent(enable, clickable, clickEnable)
76 | addComponent()
77 | }
78 |
79 | /**
80 | * addComponent
81 | */
82 | private fun addComponent() {
83 | this.add(mEnableCheckBox)
84 | this.add(mIdJLabel)
85 | this.add(mClickCheckBox)
86 | this.add(mFieldJTextField)
87 | }
88 |
89 | /**
90 | * 设置Component
91 |
92 | * @param enable enable
93 | *
94 | * @param clickable clickable
95 | *
96 | * @param clickEnable clickEnable
97 | */
98 | private fun initComponent(enable: Boolean, clickable: Boolean, clickEnable: Boolean) {
99 | mEnableCheckBox.isSelected = enable
100 | if (clickEnable) {
101 | mClickCheckBox.isSelected = clickable
102 | mClickCheckBox.isEnabled = enable
103 | } else {
104 | mClickCheckBox.isEnabled = false
105 | }
106 |
107 | mIdJLabel.isEnabled = enable
108 | mFieldJTextField.isEnabled = enable
109 |
110 | // 设置左对齐
111 | mEnableCheckBox.horizontalAlignment = JLabel.LEFT
112 | mIdJLabel.horizontalAlignment = JLabel.LEFT
113 | mFieldJTextField.horizontalAlignment = JTextField.LEFT
114 | // 监听
115 | mEnableCheckBox.addActionListener {
116 | mEnableListener?.let {
117 | mEnableListener?.invoke(mEnableCheckBox)
118 | mIdJLabel.isEnabled = mEnableCheckBox.isSelected
119 | if (clickEnable) mClickCheckBox.isEnabled = mEnableCheckBox.isSelected
120 | mFieldJTextField.isEnabled = mEnableCheckBox.isSelected
121 | }
122 | }
123 | // 监听
124 | mClickCheckBox.addActionListener {
125 | mClickListener?.invoke(mClickCheckBox)
126 | }
127 | // 监听
128 | mFieldJTextField.addFocusListener(object : FocusListener {
129 | override fun focusGained(e: FocusEvent) {
130 | mFieldFocusListener?.invoke(mFieldJTextField)
131 | }
132 |
133 | override fun focusLost(e: FocusEvent) {
134 | mFieldFocusListener?.invoke(mFieldJTextField)
135 | }
136 | })
137 | }
138 |
139 | /**
140 | * 设置布局相关
141 |
142 | * @param layout layout
143 | *
144 | * @param emptyBorder emptyBorder
145 | */
146 | private fun initLayout(layout: LayoutManager, emptyBorder: EmptyBorder) {
147 | // 设置布局内容
148 | this.layout = layout
149 | // 设置border
150 | this.border = emptyBorder
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/utils/CreateMethodCreator.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import com.intellij.codeInsight.actions.ReformatCodeProcessor
4 | import com.intellij.openapi.command.WriteCommandAction.Simple
5 | import com.intellij.openapi.editor.Editor
6 | import com.intellij.openapi.project.Project
7 | import com.intellij.psi.JavaPsiFacade
8 | import com.intellij.psi.PsiClass
9 | import com.intellij.psi.PsiElementFactory
10 | import com.intellij.psi.PsiFile
11 | import com.intellij.psi.codeStyle.JavaCodeStyleManager
12 | import constant.Constant
13 | import createFragmentInitViewMethod
14 | import createFragmentOnCreateMethod
15 | import createInitViewMethod
16 | import createOnCreateMethod
17 | import createOnCreateViewMethod
18 | import createOnDestroyViewMethod
19 | import isExtendsActivityOrActivityCompat
20 | import isExtendsFragmentOrFragmentV4
21 | import showPopupBalloon
22 |
23 | /**
24 | * 生成Activity的onCreate方法和Fragment的onCreateView方法
25 | * @author Jowan
26 | */
27 | class CreateMethodCreator(private val mEditor: Editor,
28 | private val mFile: PsiFile,
29 | private val mClass: PsiClass,
30 | command: String,
31 | private val mSelectedText: String, // activity/fragment
32 | private val mType: String,
33 | private val mIsButterKnife: Boolean) :
34 | Simple(mClass.project, command) {
35 | private val mProject: Project = mClass.project
36 | private val mFactory: PsiElementFactory
37 |
38 | init {
39 | // 获取Factory
40 | mFactory = JavaPsiFacade.getElementFactory(mProject)
41 | }
42 |
43 | @Throws(Throwable::class)
44 | override fun run() {
45 | try {
46 | createMethod()
47 | } catch (e: Exception) {
48 | mEditor.showPopupBalloon(e.message, 10)
49 | return
50 | }
51 |
52 | // 重写class
53 | val styleManager = JavaCodeStyleManager.getInstance(mProject)
54 | styleManager.optimizeImports(mFile)
55 | styleManager.shortenClassReferences(mClass)
56 | ReformatCodeProcessor(mProject, mClass.containingFile, null, false).runWithoutProgress()
57 | var actionName = Constant.ACTION_FINDVIEWBYID
58 | if (mIsButterKnife) {
59 | actionName = Constant.ACTION_BUTTERKNIFE
60 | }
61 | if (mType == Constant.CLASS_TYPE_BY_ACTIVITY) {
62 | mEditor.showPopupBalloon(Constant.Ext.CREATOR_NO_ONCREATE_METHOD + actionName, 10)
63 | } else if (mType == Constant.CLASS_TYPE_BY_FRAGMENT) {
64 | mEditor.showPopupBalloon(Constant.Ext.CREATOR_NO_ONCREATEVIEW_METHOD + actionName, 10)
65 | }
66 | }
67 |
68 | /**
69 | * 设置Activity的onCreate方法和Fragment的onCreateView方法
70 | */
71 | private fun createMethod() {
72 | if (mProject isExtendsActivityOrActivityCompat mClass) {
73 | // 判断是否有onCreate方法
74 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
75 | // 添加
76 | mClass.add(mFactory.createMethodFromText(mSelectedText.createOnCreateMethod(mIsButterKnife), mClass))
77 | if (!mIsButterKnife && mClass.findMethodsByName(Constant.Ext.CREATOR_INITVIEW_NAME, false).isEmpty()) {
78 | mClass.add(mFactory.createMethodFromText(createInitViewMethod(), mClass))
79 | }
80 | }
81 |
82 | } else if (mProject isExtendsFragmentOrFragmentV4 mClass) {
83 | var isViewExist = false
84 | var isUnbinderExist = false
85 | for (psiField in mClass.fields) {
86 | if (psiField.name != null && psiField.name == Constant.Ext.CREATOR_VIEW_NAME) {
87 | isViewExist = true
88 | }
89 | if (psiField.name != null && psiField.name == Constant.Ext.CREATOR_UNBINDER_NAME) {
90 | isUnbinderExist = true
91 | }
92 | }
93 | if (!isViewExist) {
94 | mClass.add(mFactory.createFieldFromText("private View view;", mClass))
95 | }
96 | if (mIsButterKnife && !isUnbinderExist) {
97 | mClass.add(mFactory.createFieldFromText("private Unbinder unbinder;", mClass))
98 | }
99 | // 判断是否有onCreate方法
100 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
101 | mClass.add(mFactory.createMethodFromText(mSelectedText.createFragmentOnCreateMethod(), mClass))
102 | } else {
103 | // onCreate是否存在View.inflate方法
104 | var hasInflateStatement = false
105 | val onCreate = mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false)[0]
106 | if (onCreate.body != null) {
107 | for (psiStatement in onCreate.body!!.statements) {
108 | if (psiStatement.text.contains("View.inflate(getActivity(), R.layout.$mSelectedText, null);")) {
109 | hasInflateStatement = true
110 | break
111 | } else {
112 | hasInflateStatement = false
113 | }
114 | }
115 | if (!hasInflateStatement) {
116 | onCreate.body?.add(mFactory.createStatementFromText("view = View.inflate(getActivity(), R.layout.$mSelectedText, null);", mClass))
117 | }
118 | }
119 | }
120 | // 判断是否有onCreateView方法
121 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false).isEmpty()) {
122 | // 添加
123 | mClass.add(mFactory.createMethodFromText(createOnCreateViewMethod(mIsButterKnife), mClass))
124 | if (!mIsButterKnife && mClass.findMethodsByName(Constant.Ext.CREATOR_INITVIEW_NAME, false).isEmpty()) {
125 | mClass.add(mFactory.createMethodFromText(createFragmentInitViewMethod(), mClass))
126 | }
127 | }
128 | // ButterKnife判断是否有onDestroyView方法
129 | if (mIsButterKnife) {
130 | if (mClass.findMethodsByName(Constant.Ext.CREATOR_ONDESTROYVIEW_METHOD, false).isEmpty()) {
131 | mClass.add(mFactory.createMethodFromText(createOnDestroyViewMethod(), mClass))
132 | } else {
133 | // onDestroyView是否存在unbinder.unbind();
134 | var hasUnbinderStatement = false
135 | val onCreate = mClass.findMethodsByName(Constant.Ext.CREATOR_ONDESTROYVIEW_METHOD, false)[0]
136 | if (onCreate.body != null) {
137 | for (psiStatement in onCreate.body!!.statements) {
138 | if (psiStatement.text.contains(Constant.Ext.CREATOR_UNBINDER_FIELD)) {
139 | hasUnbinderStatement = true
140 | break
141 | } else {
142 | hasUnbinderStatement = false
143 | }
144 | }
145 | if (!hasUnbinderStatement) {
146 | onCreate.body?.add(mFactory.createStatementFromText(Constant.Ext.CREATOR_UNBINDER_FIELD, mClass))
147 | }
148 | }
149 | }
150 | }
151 |
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/utils/GenerateCreator.kt:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import com.intellij.codeInsight.actions.ReformatCodeProcessor
4 | import com.intellij.openapi.command.WriteCommandAction.Simple
5 | import com.intellij.openapi.editor.Editor
6 | import com.intellij.openapi.project.Project
7 | import com.intellij.psi.*
8 | import com.intellij.psi.codeStyle.JavaCodeStyleManager
9 | import constant.Constant
10 | import createButterKnifeOnClickMethodAndSwitch
11 | import createButterKnifeViewHolder
12 | import createButterKnifeViewMethod
13 | import createFieldByElement
14 | import createFieldText
15 | import createFieldsByInitViewMethod
16 | import createFindViewByIdOnClickMethodAndSwitch
17 | import createFindViewByIdViewHolder
18 | import createOnClickAnnotation
19 | import createOnClickValue
20 | import createOnCreateMethod
21 | import createOnCreateViewMethod
22 | import createOnDestroyViewMethod
23 | import createSwitchByOnClickMethod
24 | import entitys.Element
25 | import getOnClickListById
26 | import getPsiMethodByButterKnifeOnClick
27 | import getPsiMethodByButterKnifeOnClickValue
28 | import getPsiMethodByName
29 | import getPsiMethodParamsViewField
30 | import isExtendsActivityOrActivityCompat
31 | import isExtendsFragmentOrFragmentV4
32 | import isImplementsOnClickListener
33 | import layoutInflaterType2Str
34 | import showPopupBalloon
35 | import views.GenerateDialog
36 | import java.util.*
37 |
38 |
39 | /**
40 | * 生成代码
41 | * @author Jowan
42 | */
43 | class GenerateCreator(
44 | private val mDialog: GenerateDialog,
45 | private val mEditor: Editor,
46 | private val mFile: PsiFile,
47 | private val mClass: PsiClass,
48 | private val mProject: Project,
49 | private val mElements: ArrayList,
50 | private val mFactory: PsiElementFactory,
51 | private val mSelectedText: String,
52 | private val mIsLayoutInflater: Boolean,
53 | private val mLayoutInflaterText: String,
54 | private val mLayoutInflaterType: Int,
55 | private val mIsButterKnife: Boolean,
56 | private val mIsBind: Boolean,
57 | private val mViewHolder: Boolean,
58 | command: String,
59 | private val mNeedCasts: Boolean
60 | ) : Simple(mClass.project, command) {
61 |
62 | private val mOnClickList = ArrayList()
63 |
64 | init {
65 | if (mIsButterKnife) {
66 | mElements.filterTo(mOnClickList) {
67 | it.isEnable && it.isClickable
68 | }
69 | }
70 | }
71 |
72 | @Throws(Throwable::class)
73 | override fun run() {
74 | try {
75 | if (mViewHolder) {
76 | generateViewHolder(mIsButterKnife)
77 | } else {
78 | if (mIsButterKnife) {
79 | generateButterKnife()
80 | } else {
81 | generateFindViewById()
82 | }
83 | }
84 | } catch (e: Exception) {
85 | e.printStackTrace()
86 | // 异常打印
87 | mDialog.cancelDialog()
88 | mEditor.showPopupBalloon(e.message, 10)
89 | return
90 | }
91 |
92 | // 重写class
93 | val styleManager = JavaCodeStyleManager.getInstance(mProject)
94 | styleManager.optimizeImports(mFile)
95 | styleManager.shortenClassReferences(mClass)
96 | ReformatCodeProcessor(mProject, mClass.containingFile, null, false).runWithoutProgress()
97 | mEditor.showPopupBalloon(Constant.Action.SELECTED_SUCCESS, 5)
98 | }
99 |
100 | /**
101 | * 创建ViewHolder
102 |
103 | * @param isButterKnife true FindViewById
104 | * false ButterKnife
105 | */
106 | private fun generateViewHolder(isButterKnife: Boolean) {
107 | val viewHolderName = "ViewHolder"
108 | val viewHolderRootView = "view"
109 | val str: String
110 | str = if (isButterKnife) {
111 | viewHolderName.createButterKnifeViewHolder(viewHolderRootView, mElements)
112 | } else {
113 | viewHolderName.createFindViewByIdViewHolder(viewHolderRootView, mElements, mNeedCasts)
114 | }
115 | // 创建ViewHolder类
116 | val viewHolder = mFactory.createClassFromText(str, mClass)
117 | // 设置名字
118 | viewHolder.setName(viewHolderName)
119 | // 添加ViewHolder类到类中
120 | mClass.add(viewHolder)
121 | // 添加static
122 | mClass.addBefore(mFactory.createKeyword("static"), mClass.findInnerClassByName(viewHolderName, true))
123 | }
124 |
125 | /**
126 | * 设置变量的值FindViewById,Activity和Fragment
127 | */
128 | private fun generateFindViewById() {
129 | if (mProject isExtendsActivityOrActivityCompat mClass) {
130 | // 判断是否有onCreate方法
131 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
132 | // 添加
133 | mClass.add(mFactory.createMethodFromText(mSelectedText.createOnCreateMethod(false), mClass))
134 | return
135 | }
136 | generateFindViewByIdFields()
137 | // 获取setContentView
138 | var setContentViewStatement: PsiStatement? = null
139 | // onCreate是否存在initView方法
140 | var hasInitViewStatement = false
141 | // 获取onCreate方法对象
142 | val onCreate = mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false)[0]
143 | if (onCreate.body != null) {
144 | for (psiStatement in onCreate.body!!.statements) {
145 | // 查找setContentView
146 | if (psiStatement.firstChild is PsiMethodCallExpression) {
147 | val methodExpression = (psiStatement.firstChild as PsiMethodCallExpression).methodExpression
148 | if (methodExpression.text == Constant.Ext.CREATOR_SETCONTENTVIEW_METHOD) {
149 | setContentViewStatement = psiStatement
150 | } else if (methodExpression.text == Constant.Ext.CREATOR_INITVIEW_NAME) {
151 | hasInitViewStatement = true
152 | }
153 | }
154 | }
155 | if (setContentViewStatement == null) {
156 | onCreate.body?.add(mFactory.createStatementFromText("setContentView(R.layout.$mSelectedText);", mClass))
157 | }
158 |
159 | if (!hasInitViewStatement) {
160 | // 将initView()写到setContentView()后面
161 | setContentViewStatement?.let {
162 | onCreate.body?.addAfter(mFactory.createStatementFromText("initView();", mClass), it)
163 | } ?: let {
164 | onCreate.body?.add(mFactory.createStatementFromText("initView();", mClass))
165 | }
166 | }
167 | }
168 |
169 | generateFindViewByIdLayoutCode(null, "getApplicationContext()")
170 |
171 | return
172 | }
173 | if (mProject isExtendsFragmentOrFragmentV4 mClass) {
174 | var isViewExist = false
175 | for (psiField in mClass.fields) {
176 | if (psiField.text != null && psiField.text == "private View view;") {
177 | isViewExist = true
178 | break
179 | } else {
180 | isViewExist = false
181 | }
182 | }
183 | if (!isViewExist) {
184 | mClass.add(mFactory.createFieldFromText("private View view;", mClass))
185 | }
186 | // 判断是否有onCreateView方法
187 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false).isEmpty()) {
188 | // 添加
189 | mClass.add(mFactory.createMethodFromText(createOnCreateViewMethod(false), mClass))
190 | return
191 | }
192 | generateFindViewByIdFields()
193 | // 查找onCreateView
194 | var returnStatement: PsiReturnStatement? = null
195 | // view
196 | var returnValue: String? = null
197 | // onCreateView是否存在initView方法
198 | var hasInitViewStatement = false
199 |
200 | val onCreate = mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false)[0]
201 | if (onCreate.body != null) {
202 | for (psiStatement in onCreate.body!!.statements) {
203 | if (psiStatement is PsiReturnStatement) {
204 | // 获取view的值
205 | returnStatement = psiStatement
206 | if (returnStatement.returnValue != null) {
207 | returnValue = returnStatement.returnValue?.text
208 | }
209 | } else if (psiStatement.firstChild is PsiMethodCallExpression) {
210 | val methodExpression = (psiStatement.firstChild as PsiMethodCallExpression).methodExpression
211 | if (methodExpression.text == Constant.Ext.CREATOR_INITVIEW_NAME) {
212 | hasInitViewStatement = true
213 | }
214 | }
215 | }
216 |
217 | if (!hasInitViewStatement && returnStatement != null && returnValue != null) {
218 | onCreate.body?.addBefore(mFactory.createStatementFromText("initView($returnValue);", mClass), returnStatement)
219 | }
220 | }
221 | generateFindViewByIdLayoutCode(returnValue, "getActivity()")
222 | }
223 | }
224 |
225 | /**
226 | * 创建变量
227 | */
228 | private fun generateFindViewByIdFields() {
229 | if (mIsLayoutInflater) {
230 | val inflater = "private View $mLayoutInflaterText;"
231 | // 已存在的变量就不创建
232 | var duplicateField = false
233 | for (field in mClass.fields) {
234 | val name = field.name
235 | if (name != null && name == mLayoutInflaterText) {
236 | duplicateField = true;
237 | break;
238 | }
239 | }
240 | if (!duplicateField) {
241 | mClass.add(mFactory.createFieldFromText(inflater, mClass))
242 | }
243 | }
244 | for (element in mElements) {
245 | // 已存在的变量就不创建
246 | val fields = mClass.fields
247 | var duplicateField = false
248 | for (field in fields) {
249 | if (!mIsLayoutInflater) {
250 | if (field.name != null && field.name == element.fieldName) {
251 | duplicateField = true
252 | break
253 | }
254 | } else {
255 | val layoutField = element.fieldName + layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType)
256 | if (field.name != null && field.name == layoutField) {
257 | duplicateField = true
258 | break
259 | }
260 | }
261 | }
262 | // 已存在跳出
263 | if (duplicateField) {
264 | continue
265 | }
266 | // 设置变量名,获取text里面的内容
267 | if (element.isEnable) {
268 | // 添加到class
269 | mClass.add(mFactory.createFieldFromText(mProject.createFieldText(element, mFile).createFieldByElement(
270 | element, mIsLayoutInflater, mLayoutInflaterText, mLayoutInflaterType), mClass))
271 | }
272 | }
273 | }
274 |
275 | /**
276 | * 写initView方法
277 |
278 | * @param findPre Fragment的话要view.findViewById
279 | *
280 | * @param context context
281 | */
282 | private fun generateFindViewByIdLayoutCode(findPre: String?, context: String) {
283 | // 判断是否已有initView方法
284 | val initViewMethods = mClass.findMethodsByName(Constant.Ext.CREATOR_INITVIEW_NAME, false)
285 | // 有initView方法
286 | if (initViewMethods.isNotEmpty() && initViewMethods[0].body != null) {
287 | val initViewMethodBody = initViewMethods[0].body
288 | // 获取initView方法里面的每条内容
289 | val statements = initViewMethodBody!!.statements
290 | if (mIsLayoutInflater) {
291 | // 添加LayoutInflater.from(this).inflate(R.layout.activity_main, null);
292 | val layoutInflater = "$mLayoutInflaterText = LayoutInflater.from($context).inflate(R.layout.$mSelectedText, null);"
293 | // 判断是否存在
294 | var isExist = false
295 | for (statement in statements) {
296 | if (statement.text == layoutInflater) {
297 | isExist = true
298 | break
299 | } else {
300 | isExist = false
301 | }
302 | }
303 | // 不存在才添加
304 | if (!isExist) {
305 | initViewMethodBody.add(mFactory.createStatementFromText(layoutInflater, initViewMethods[0]))
306 | }
307 | }
308 | for (element in mElements) {
309 | if (element.isEnable) {
310 | // 判断是否已存在findViewById
311 | var isFdExist = false
312 | var pre = findPre?.let { it + "." } ?: ""
313 | var inflater = ""
314 | if (mIsLayoutInflater) {
315 | inflater = layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType)
316 | pre = mLayoutInflaterText + "."
317 | }
318 | val casts = if(mNeedCasts) " (${element.name}) " else ""
319 | val findViewById = "${element.fieldName}$inflater =$casts${pre}findViewById(${element.fullID});"
320 | for (statement in statements) {
321 | if (statement.text == findViewById) {
322 | isFdExist = true
323 | break
324 | } else {
325 | isFdExist = false
326 | }
327 | }
328 | // 不存在就添加
329 | if (!isFdExist) {
330 | initViewMethodBody.add(mFactory.createStatementFromText(findViewById, initViewMethods[0]))
331 | }
332 | if (element.isClickEnable) {
333 | // 判断是否已存在setOnClickListener
334 | var isClickExist = false
335 | val setOnClickListener = "${element.fieldName}$inflater.setOnClickListener(this);"
336 | for (statement in statements) {
337 | if (statement.text == setOnClickListener) {
338 | isClickExist = true
339 | break
340 | } else {
341 | isClickExist = false
342 | }
343 | }
344 | if (!isClickExist && element.isClickable) {
345 | initViewMethodBody.add(mFactory.createStatementFromText(setOnClickListener, initViewMethods[0]))
346 | }
347 | }
348 | }
349 | }
350 | } else {
351 | mClass.add(mFactory.createMethodFromText(
352 | createFieldsByInitViewMethod(findPre, mIsLayoutInflater, mLayoutInflaterText, context, mSelectedText, mElements, mLayoutInflaterType, mNeedCasts), mClass))
353 | }
354 | getFindViewByIdOnClickList()
355 | if (mOnClickList.size != 0) {
356 | generateFindViewByIdOnClickListenerCode()
357 | }
358 | }
359 |
360 | /**
361 | * 添加实现OnClickListener接口
362 | */
363 | private fun generateFindViewByIdOnClickListenerCode() {
364 | // 获取已实现的接口
365 | val implementsList = mClass.implementsList
366 | var isImplOnClick = false
367 | if (implementsList != null) {
368 | // 获取列表
369 | val referenceElements = implementsList.referenceElements
370 | // 是否实现了OnClickListener接口
371 | isImplOnClick = isImplementsOnClickListener(referenceElements)
372 | }
373 | // 未实现添加OnClickListener接口
374 | if (!isImplOnClick) {
375 | val referenceElementByFQClassName = mFactory.createReferenceElementByFQClassName("android.view.View.OnClickListener", mClass.resolveScope)
376 | // 添加的PsiReferenceList
377 | implementsList?.add(referenceElementByFQClassName)
378 | }
379 | generateFindViewByIdClickCode()
380 | }
381 |
382 | /**
383 | * 获取有OnClick属性的Element
384 | */
385 | private fun getFindViewByIdOnClickList() {
386 | mElements.filterTo(mOnClickList) { it.isEnable && it.isClickEnable && it.isClickable }
387 | }
388 |
389 | /**
390 | * 写onClick方法
391 | */
392 | private fun generateFindViewByIdClickCode() {
393 | // 判断是否已有onClick方法
394 | val onClickMethods = mClass.findMethodsByName(Constant.FIELD_ONCLICK, false)
395 | // 判断是否包含@OnClick注解
396 | val butterKnifeOnClickMethod = mClass.getPsiMethodByButterKnifeOnClick()
397 | // 已有onClick方法
398 | if (butterKnifeOnClickMethod == null && onClickMethods.isNotEmpty() && onClickMethods[0].body != null) {
399 | val onClickMethodBody = onClickMethods[0].body
400 | // 获取switch
401 | for (psiElement in onClickMethodBody!!.children) {
402 | if (psiElement is PsiSwitchStatement) {
403 | // 获取switch的内容
404 | val psiSwitchStatementBody = psiElement.body
405 | if (psiSwitchStatementBody != null) {
406 | for (element in mOnClickList) {
407 | val cass = "case " + element.fullID + ":"
408 | // 判断是否存在
409 | var isExist = false
410 | for (statement in psiSwitchStatementBody.statements) {
411 | if (statement.text.replace("\n".toRegex(), "").replace("break;".toRegex(), "") == cass) {
412 | isExist = true
413 | break
414 | } else {
415 | isExist = false
416 | }
417 | }
418 | // 不存在就添加
419 | if (!isExist) {
420 | psiSwitchStatementBody.add(mFactory.createStatementFromText(cass, psiSwitchStatementBody))
421 | psiSwitchStatementBody.add(mFactory.createStatementFromText("break;", psiSwitchStatementBody))
422 | }
423 | }
424 | }
425 | }
426 | }
427 | return
428 | }
429 | if (mOnClickList.size != 0) {
430 | mClass.add(mFactory.createMethodFromText(mOnClickList.createFindViewByIdOnClickMethodAndSwitch(), mClass))
431 | }
432 | }
433 |
434 | /**
435 | * 设置变量,Activity和Fragment
436 | */
437 | private fun generateButterKnife() {
438 | if (mProject isExtendsActivityOrActivityCompat mClass) {
439 | // 判断是否有onCreate方法
440 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false).isEmpty()) {
441 | // 添加
442 | mClass.add(mFactory.createMethodFromText(mSelectedText.createOnCreateMethod(true), mClass))
443 | } else {
444 | generateButterKnifeFields(Constant.CLASS_TYPE_BY_ACTIVITY)
445 | if (mIsLayoutInflater) {
446 | generateButterKnifeViewMethod("getApplicationContext()")
447 | }
448 | // 获取setContentView
449 | var setContentViewStatement: PsiStatement? = null
450 | // onCreate是否ButterKnife.bind(this);
451 | var hasButterKnifeBindStatement = false
452 |
453 | val onCreate = mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATE, false)[0]
454 | if (onCreate.body != null) {
455 | for (psiStatement in onCreate.body!!.statements) {
456 | // 查找setContentView
457 | if (psiStatement.firstChild is PsiMethodCallExpression) {
458 | val methodExpression = (psiStatement.firstChild as PsiMethodCallExpression).methodExpression
459 | if (methodExpression.text == Constant.Ext.CREATOR_SETCONTENTVIEW_METHOD) {
460 | setContentViewStatement = psiStatement
461 | } else if (methodExpression.text.contains(Constant.Ext.FIELD_BUTTERKNIFE_BIND)) {
462 | hasButterKnifeBindStatement = true
463 | }
464 | }
465 | }
466 | if (setContentViewStatement == null) {
467 | onCreate.body?.add(mFactory.createStatementFromText("setContentView(R.layout.$mSelectedText);", mClass))
468 | }
469 |
470 | if (!hasButterKnifeBindStatement) {
471 | // 将ButterKnife.bind(this);写到setContentView()后面
472 | if (setContentViewStatement != null) {
473 | onCreate.body?.addAfter(mFactory.createStatementFromText("ButterKnife.bind(this);", mClass), setContentViewStatement)
474 | } else {
475 | onCreate.body?.add(mFactory.createStatementFromText("ButterKnife.bind(this);", mClass))
476 | }
477 | }
478 | }
479 | }
480 | if (mOnClickList.size > 0 && !mIsLayoutInflater) {
481 | generateButterKnifeClickCode()
482 | }
483 | return
484 | }
485 | if (mProject isExtendsFragmentOrFragmentV4 mClass) {
486 | // 判断是否有onCreateView方法
487 | if (mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false).isEmpty()) {
488 | // 添加
489 | mClass.add(mFactory.createMethodFromText(createOnCreateViewMethod(true), mClass))
490 |
491 | } else {
492 | generateButterKnifeFields(Constant.CLASS_TYPE_BY_FRAGMENT)
493 | if (mIsLayoutInflater) {
494 | generateButterKnifeViewMethod("getActivity()")
495 | }
496 | // 查找onCreateView
497 | var returnStatement: PsiReturnStatement? = null
498 | // view
499 | var returnValue: String? = null
500 |
501 | val onCreateView = mClass.findMethodsByName(Constant.PSI_METHOD_BY_ONCREATEVIEW, false)[0]
502 | if (onCreateView.body != null) {
503 | for (psiStatement in onCreateView.body!!.statements) {
504 | if (psiStatement is PsiReturnStatement) {
505 | // 获取view的值
506 | returnStatement = psiStatement
507 | if (returnStatement.returnValue != null) {
508 | returnValue = returnStatement.returnValue?.text
509 | }
510 | }
511 | }
512 |
513 | if (returnStatement != null && returnValue != null) {
514 | var isBindExist = false
515 | // 判断是否已存在unbinder = ButterKnife.bind
516 | val bind = "unbinder = ButterKnife.bind(this, $returnValue);"
517 | // 获取onCreateView方法里面的PsiStatement
518 | for (psiStatement in onCreateView.body!!.statements) {
519 | if (psiStatement.text.contains(bind)) {
520 | isBindExist = true
521 | break
522 | } else {
523 | isBindExist = false
524 | }
525 | }
526 | if (!isBindExist && mIsBind) {
527 | //将ButterKnife.bind(this);写到return view前面
528 | onCreateView.body?.addBefore(mFactory.createStatementFromText(bind, mClass), returnStatement)
529 | }
530 | }
531 | }
532 | }
533 | if (mOnClickList.size > 0 && !mIsLayoutInflater) {
534 | generateButterKnifeClickCode()
535 | }
536 | if (mIsBind) {
537 | generateButterKnifeLayoutCode()
538 | }
539 | }
540 | }
541 |
542 | /**
543 | * 创建变量
544 | *
545 | * @param type type
546 | */
547 | private fun generateButterKnifeFields(type: String) {
548 | if (type == Constant.CLASS_TYPE_BY_FRAGMENT) {
549 | // 判断View是否存在
550 | var isViewExist = false
551 | // 判断Unbinder是否存在
552 | var isUnBinderExist = false
553 | for (field in mClass.fields) {
554 | if (field.name != null && field.name == Constant.Ext.CREATOR_VIEW_NAME) {
555 | isViewExist = true
556 | }
557 | if (field.name != null && field.name == Constant.Ext.CREATOR_UNBINDER_NAME) {
558 | isUnBinderExist = true
559 | }
560 | }
561 | if (!isViewExist) {
562 | mClass.add(mFactory.createFieldFromText("private View view;", mClass))
563 | }
564 | if (!isUnBinderExist && mIsBind) {
565 | mClass.add(mFactory.createFieldFromText("private Unbinder unbinder;", mClass))
566 | }
567 | }
568 | if (mIsLayoutInflater) {
569 | val inflater = "private View $mLayoutInflaterText;"
570 | // 已存在的变量就不创建
571 | var duplicateField = false
572 | for (field in mClass.fields) {
573 | if (field.name != null && field.name == mLayoutInflaterText) {
574 | duplicateField = true
575 | break
576 | }
577 | }
578 | if (!duplicateField) {
579 | mClass.add(mFactory.createFieldFromText(inflater, mClass))
580 | }
581 | }
582 | mElements.filter {
583 | it.isEnable
584 | }.forEach { element ->
585 | if (mIsLayoutInflater) {
586 | val layoutField = element.fieldName + layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType)
587 | var duplicateField = false
588 | for (field in mClass.fields) {
589 | if (field.name != null && field.name == layoutField) {
590 | duplicateField = true
591 | }
592 | }
593 | // 已存在跳出
594 | if (!duplicateField) {
595 | // 添加到class
596 | mClass.add(mFactory.createFieldFromText(mProject.createFieldText(element, mFile).createFieldByElement(
597 | element, true, mLayoutInflaterText, mLayoutInflaterType), mClass))
598 | }
599 | } else {
600 | // 已存在的变量就不创建
601 | var isFieldExist = false
602 | var isAnnotationExist = false
603 | for (field in mClass.fields) {
604 | if (field.name != null) {
605 | isAnnotationExist = field.text.contains("@BindView(${element.fullID})")
606 | isFieldExist = field.text.contains("${element.name} ${element.fieldName};")
607 | && !(field.text.contains("private") || field.text.contains("static"))
608 | if (isAnnotationExist || isFieldExist) {
609 | break
610 | }
611 | }
612 | }
613 | // @BindView(R.id.text) TextView mText;
614 | // 如果两个都存在则跳出
615 | if (isAnnotationExist && isFieldExist) {
616 | return@forEach
617 | }
618 | if (!isFieldExist) {
619 | // 如果只存在TextView mText
620 | val fromText = element.name + " " + element.fieldName + ";"
621 | // 添加到class
622 | mClass.add(mFactory.createFieldFromText(fromText, mClass))
623 | }
624 |
625 | for (field in mClass.fields) {
626 | if (field.name != null && field.text.contains("${element.name} ${element.fieldName};")
627 | && !(field.text.contains("private") || field.text.contains("static"))) {
628 | val annotationText = "@BindView(" + element.fullID + ")"
629 | // 添加注解到field
630 | mClass.addBefore(mFactory.createAnnotationFromText(annotationText, mClass), field)
631 | break
632 | }
633 | }
634 | }
635 | }
636 | }
637 |
638 | /**
639 | * 写onClick方法
640 | */
641 | private fun generateButterKnifeClickCode() {
642 | // 判断是否包含@OnClick注解
643 | val butterKnifeOnClickMethod = mClass.getPsiMethodByButterKnifeOnClick()
644 | // 有@OnClick注解
645 | if (butterKnifeOnClickMethod != null && butterKnifeOnClickMethod.body != null) {
646 | val onClickMethodBody = butterKnifeOnClickMethod.body
647 | // 获取switch
648 | var psiSwitchStatement: PsiSwitchStatement? = null
649 | for (psiElement in onClickMethodBody!!.children) {
650 | if (psiElement is PsiSwitchStatement) {
651 | psiSwitchStatement = psiElement
652 | break
653 | }
654 | }
655 | val psiMethodByButterKnifeOnClickValue = mClass.getPsiMethodByButterKnifeOnClickValue()
656 | val onClickIdList = getOnClickListById(mOnClickList)
657 | val onClickValues = createOnClickValue(psiMethodByButterKnifeOnClickValue, onClickIdList)
658 | // 有switch
659 | if (psiSwitchStatement != null) {
660 | // 获取switch的内容
661 | val psiSwitchStatementBody = psiSwitchStatement.body
662 | if (psiSwitchStatementBody != null) {
663 | for (onClickValue in onClickValues) {
664 | val cass = "case $onClickValue:"
665 | // 判断是否存在
666 | var isExist = false
667 | for (statement in psiSwitchStatementBody.statements) {
668 | if (statement.text.replace("\n".toRegex(), "").replace("break;".toRegex(), "") == cass) {
669 | isExist = true
670 | break
671 | } else {
672 | isExist = false
673 | }
674 | }
675 | // 不存在就添加
676 | if (!isExist) {
677 | psiSwitchStatementBody.add(mFactory.createStatementFromText(cass, psiSwitchStatementBody))
678 | psiSwitchStatementBody.add(mFactory.createStatementFromText("break;", psiSwitchStatementBody))
679 | }
680 | }
681 | }
682 | } else {
683 | // 没有switch
684 | val psiMethodParamsViewField = mClass.getPsiMethodParamsViewField()
685 | if (psiMethodParamsViewField != null) {
686 | butterKnifeOnClickMethod.body?.add(mFactory.createStatementFromText(
687 | psiMethodParamsViewField.createSwitchByOnClickMethod(onClickValues), butterKnifeOnClickMethod))
688 | }
689 | }
690 | mClass.createOnClickAnnotation(mFactory, onClickValues)
691 | return
692 | }
693 | if (mOnClickList.size != 0) {
694 | mClass.add(mFactory.createMethodFromText(mOnClickList.createButterKnifeOnClickMethodAndSwitch(), mClass))
695 | }
696 | }
697 |
698 | /**
699 | * 写onDestroyView方法
700 | */
701 | private fun generateButterKnifeLayoutCode() {
702 | // 判断是否已有onDestroyView方法
703 | val onDestroyViewMethods = mClass.findMethodsByName(Constant.Ext.CREATOR_ONDESTROYVIEW_METHOD, false)
704 | // 有onDestroyView方法
705 | if (onDestroyViewMethods.isNotEmpty() && onDestroyViewMethods[0].body != null) {
706 | val onDestroyViewMethodBody = onDestroyViewMethods[0].body
707 | // 获取onDestroyView方法里面的每条内容
708 | val statements = onDestroyViewMethodBody!!.statements
709 | for (element in mElements) {
710 | if (element.isEnable) {
711 | // 判断是否已存在unbinder.unbind();
712 | var isFdExist = false
713 | for (statement in statements) {
714 | if (statement.text == Constant.Ext.CREATOR_UNBINDER_FIELD) {
715 | isFdExist = true
716 | break
717 | } else {
718 | isFdExist = false
719 | }
720 | }
721 | // 不存在就添加
722 | if (!isFdExist) {
723 | onDestroyViewMethodBody.add(mFactory.createStatementFromText(Constant.Ext.CREATOR_UNBINDER_FIELD, onDestroyViewMethods[0]))
724 | break
725 | }
726 | }
727 | }
728 | return
729 | }
730 | mClass.add(mFactory.createMethodFromText(createOnDestroyViewMethod(), mClass))
731 | }
732 |
733 | /**
734 | * 创建LayoutInflater的方法,包含ButterKnife.findById(view, R.id.id)
735 | * @param context context
736 | */
737 | private fun generateButterKnifeViewMethod(context: String) {
738 | val viewMethodName = "init" + layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType)
739 | val viewMethod = mClass.getPsiMethodByName(viewMethodName)
740 | if (viewMethod != null && viewMethod.body != null) {
741 | val body = viewMethod.body
742 | // 获取initView方法里面的每条内容
743 | val statements = body!!.statements
744 | for (element in mElements) {
745 | if (element.isEnable) {
746 | // 判断是否已存在findViewById
747 | var isFdExist = false
748 | var inflater = ""
749 | if (mIsLayoutInflater) {
750 | inflater = layoutInflaterType2Str(mLayoutInflaterText, mLayoutInflaterType)
751 | }
752 | val findViewById = "${element.fieldName}$inflater = ButterKnife.findById($mLayoutInflaterText, ${element.fullID});"
753 | for (statement in statements) {
754 | if (statement.text == findViewById) {
755 | isFdExist = true
756 | break
757 | } else {
758 | isFdExist = false
759 | }
760 | }
761 | // 不存在就添加
762 | if (!isFdExist) {
763 | body.add(mFactory.createStatementFromText(findViewById, viewMethod))
764 | }
765 | }
766 | }
767 | return
768 | }
769 | mClass.add(mFactory.createMethodFromText(
770 | createButterKnifeViewMethod(mIsLayoutInflater, mLayoutInflaterText, context, mSelectedText, mElements, viewMethodName, mLayoutInflaterType), mClass))
771 | }
772 |
773 | }
774 |
--------------------------------------------------------------------------------
/src/views/ButterKnifeDialog.kt:
--------------------------------------------------------------------------------
1 | package views
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.psi.*
6 | import entitys.Element
7 | import getPsiMethodByButterKnifeOnClick
8 | import getPsiMethodByButterKnifeOnClickValue
9 | import java.util.*
10 |
11 | /**
12 | * ButterKnifeDialog
13 | * @author Jowan
14 | */
15 | class ButterKnifeDialog
16 | /**
17 | * ButterKnifeDialog
18 | */
19 | (mProject: Project,
20 | mEditor: Editor,
21 | mSelectedText: String,
22 | /*获取mElements*/
23 | private val mElements: ArrayList,
24 | /*获取当前文件*/
25 | mPsiFile: PsiFile,
26 | /*获取class*/
27 | private val mClass: PsiClass,
28 | /*判断是否全选*/
29 | private var elementSize: Int,
30 | /*判断是否是ButterKnife*/
31 | mIsButterKnife: Boolean)
32 | : GenerateDialog(mProject = mProject,
33 | mEditor = mEditor,
34 | mSelectedText = mSelectedText,
35 | elements = mElements,
36 | mPsiFile = mPsiFile,
37 | psiClass = mClass,
38 | elementSize = elementSize,
39 | mIsButterKnife = mIsButterKnife) {
40 |
41 | init {
42 | initExist()
43 | initTopPanel()
44 | initContentPanel()
45 | setCheckAll()
46 | checkBind()
47 | initBottomPanel()
48 | setConstraints()
49 | setDialog()
50 | }
51 |
52 | /**
53 | * 判断已存在的变量,设置全选
54 | * 判断onclick是否写入
55 | */
56 | private fun initExist() {
57 | // 判断是否已存在的变量
58 | var isFdExist: Boolean
59 | // 判断是否存在case R.id.id:
60 | var isCaseExist = false
61 | // 判断注解是否存在R.id.id
62 | var isAnnotationValueExist = false
63 | val fields = mClass.fields
64 | var onClickStatement: Array? = null
65 | val psiMethodByButterKnifeOnClickValue = mClass.getPsiMethodByButterKnifeOnClickValue()
66 | val psiMethodByButterKnifeOnClick = mClass.getPsiMethodByButterKnifeOnClick()
67 | if (psiMethodByButterKnifeOnClick != null && psiMethodByButterKnifeOnClick.body != null) {
68 | onClickStatement = psiMethodByButterKnifeOnClick.body?.statements
69 | }
70 | for (element in mElements) {
71 | element.isClickable = true
72 | element.setClickEnable()
73 | isFdExist = checkFieldExist(fields, element)
74 | if (onClickStatement != null) {
75 | isCaseExist = checkCaseExist(onClickStatement, element)
76 | }
77 | if (psiMethodByButterKnifeOnClickValue.size > 0) {
78 | isAnnotationValueExist = psiMethodByButterKnifeOnClickValue.contains(element.fullID)
79 | }
80 | setElementProperty(elementSize, isFdExist, isCaseExist, isAnnotationValueExist, fields, element)
81 | }
82 | }
83 |
84 | /**
85 | * 判断onClick方法里面是否包含field的case
86 | * @param onClickStatement onClick方法
87 | *
88 | * @param element element
89 | *
90 | * @return boolean
91 | */
92 | private fun checkCaseExist(onClickStatement: Array, element: Element): Boolean {
93 | val cass = "case " + element.fullID + ":"
94 | onClickStatement
95 | .filterIsInstance()
96 | .mapNotNull { // 获取switch的内容
97 | it.body
98 | }
99 | .forEach {
100 | it.statements
101 | .filter { it.text.replace("\n".toRegex(), "").replace("break;".toRegex(), "") == cass }
102 | .forEach { return true }
103 | }
104 | return false
105 | }
106 |
107 | /**
108 | * 判断变量是否包含@BindView注解
109 | * @param fields fields
110 | *
111 | * @param element element
112 | *
113 | * @return boolean
114 | */
115 | private fun checkFieldExist(fields: Array, element: Element): Boolean {
116 | fields.forEach {
117 | if (it.name != null && it.name == element.fieldName && it.text.contains("@BindView(${element.fullID})")) {
118 | return true
119 | }
120 | }
121 | return false
122 | }
123 |
124 | /**
125 | * 为已存在的变量设置checkbox
126 | * @param mElementSizeOld mElementSizeOld
127 | *
128 | * @param isFdExist 判断是否已存在的变量
129 | *
130 | * @param isCaseExist 判断是否存在case R.id.id:
131 | *
132 | * @param isAnnotationValueExist 判断注解是否存在R.id.id
133 | *
134 | * @param fields fields
135 | *
136 | * @param element element
137 | */
138 | private fun setElementProperty(mElementSizeOld: Int, isFdExist: Boolean, isCaseExist: Boolean,
139 | isAnnotationValueExist: Boolean, fields: Array, element: Element) {
140 | var mElementSize = mElementSizeOld
141 | for (field in fields) {
142 | val name = field.name
143 | if (name != null && name == element.fieldName && isFdExist) {
144 | // 已存在的变量设置checkbox为false
145 | element.isEnable = false
146 | mElementSize -= 1
147 | elementSize = mElementSize
148 | if (element.isClickEnable && (!isCaseExist || !isAnnotationValueExist)) {
149 | element.isClickable = true
150 | element.isEnable = true
151 | mElementSize += 1
152 | elementSize = mElementSize
153 | }
154 | break
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/views/FindViewByIdDialog.kt:
--------------------------------------------------------------------------------
1 | package views
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.psi.*
6 | import entitys.Element
7 | import getInitViewBodyStatements
8 | import getOnClickStatement
9 | import java.util.*
10 |
11 | /**
12 | * FindViewByIdDialog
13 | * @author Jowan
14 | */
15 | class FindViewByIdDialog
16 | /**
17 | * FindViewByIdDialog
18 | */
19 | (mProject: Project,
20 | mEditor: Editor,
21 | mSelectedText: String,
22 | /*获取mElements*/
23 | private val mElements: ArrayList,
24 | /*获取当前文件*/
25 | mPsiFile: PsiFile,
26 | /*获取class*/
27 | private val mClass: PsiClass,
28 | /*判断是否全选*/
29 | private var elementSize: Int,
30 | /*判断是否是ButterKnife*/
31 | mIsButterKnife: Boolean)
32 | : GenerateDialog(mProject = mProject,
33 | mEditor = mEditor,
34 | mSelectedText = mSelectedText,
35 | elements = mElements,
36 | mPsiFile = mPsiFile,
37 | psiClass = mClass,
38 | elementSize = elementSize,
39 | mIsButterKnife = mIsButterKnife) {
40 |
41 | init {
42 | initExist()
43 | initTopPanel()
44 | initContentPanel()
45 | setCheckAll()
46 | initBottomPanel()
47 | setConstraints()
48 | setDialog()
49 | }
50 |
51 | /**
52 | * 判断已存在的变量,设置全选
53 | * 判断onclick是否写入
54 | */
55 | private fun initExist() {
56 | // 判断是否已存在的变量
57 | var isFdExist = false
58 | // 判断是否已存在setOnClickListener
59 | var isClickExist = false
60 | // 判断是否存在case R.id.id:
61 | var isCaseExist = false
62 | val fields = mClass.fields
63 | // 获取initView方法的内容
64 | val statements = mClass.getInitViewBodyStatements()
65 | val onClickStatement = mClass.getOnClickStatement()
66 | elementSize = mElements.size
67 | for (element in mElements) {
68 | if (statements != null) {
69 | isFdExist = checkFieldExist(statements, element)
70 | val setOnClickListener = element.fieldName + ".setOnClickListener(this);"
71 | isClickExist = checkClickExist(statements, setOnClickListener)
72 | }
73 | if (onClickStatement != null) {
74 | isCaseExist = checkCaseExist(onClickStatement, element)
75 | }
76 | setElementProperty(elementSize, isFdExist, isClickExist, isCaseExist, fields, element)
77 | }
78 | }
79 |
80 | /**
81 | * 判断onClick方法里面是否包含field的case
82 | * @param onClickStatement onClick方法
83 | *
84 | * @param element element
85 | *
86 | * @return boolean
87 | */
88 | private fun checkCaseExist(onClickStatement: Array, element: Element): Boolean {
89 | val cass = "case " + element.fullID + ":"
90 | onClickStatement
91 | .filterIsInstance()
92 | .mapNotNull { // 获取switch的内容
93 | it.body
94 | }
95 | .forEach {
96 | it.statements
97 | .filter { it.text.replace("\n".toRegex(), "").replace("break;".toRegex(), "") == cass }
98 | .forEach { return true }
99 | }
100 | return false
101 | }
102 |
103 | /**
104 | * 判断initView方法里面是否包含field的findViewById
105 | * @param statements initView方法
106 | *
107 | * @param element element
108 | *
109 | * @return boolean
110 | */
111 | private fun checkFieldExist(statements: Array, element: Element): Boolean {
112 | statements.forEach {
113 | if (it.text.contains(element.fieldName)
114 | && it.text.contains("findViewById(${element.fullID});") ) {
115 | return true
116 | }
117 | }
118 | return false
119 | }
120 |
121 | /**
122 | * 判断是否setOnClickListener
123 | * @param statements onClick方法
124 | *
125 | * @param setOnClickListener setOnClickListener
126 | *
127 | * @return boolean
128 | */
129 | private fun checkClickExist(statements: Array, setOnClickListener: String): Boolean {
130 | statements.forEach {
131 | if (it.text == setOnClickListener) {
132 | return true
133 | }
134 | }
135 | return false
136 | }
137 |
138 | /**
139 | * 为已存在的变量设置checkbox
140 | * @param mElementSizes mElementSizes
141 | *
142 | * @param isFdExist 判断是否已存在的变量
143 | *
144 | * @param isClickExist 判断是否已存在setOnClickListener
145 | *
146 | * @param isCaseExist 判断是否存在case R.id.id:
147 | *
148 | * @param fields fields
149 | *
150 | * @param element element
151 | */
152 | private fun setElementProperty(mElementSizes: Int, isFdExist: Boolean, isClickExist: Boolean,
153 | isCaseExist: Boolean, fields: Array, element: Element) {
154 | var mElementSize = mElementSizes
155 | for (field in fields) {
156 | val name = field.name
157 | if (name != null && name == element.fieldName && isFdExist) {
158 | // 已存在的变量设置checkbox为false
159 | element.isEnable = false
160 | mElementSize -= 1
161 | elementSize = mElementSize
162 | if (element.isClickEnable && (!isClickExist || !isCaseExist)) {
163 | element.isClickable = true
164 | element.isEnable = true
165 | mElementSize = elementSize + 1
166 | elementSize = mElementSize
167 | }
168 | break
169 | }
170 | }
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/views/GenerateDialog.kt:
--------------------------------------------------------------------------------
1 | package views
2 |
3 | import com.intellij.openapi.editor.Editor
4 | import com.intellij.openapi.project.Project
5 | import com.intellij.psi.JavaPsiFacade
6 | import com.intellij.psi.PsiClass
7 | import com.intellij.psi.PsiFile
8 | import com.intellij.ui.components.JBScrollPane
9 | import constant.Constant
10 | import entitys.Element
11 | import entitys.IdBean
12 | import getFieldName
13 | import isButterKnifeBindExist
14 | import showPopupBalloon
15 | import utils.GenerateCreator
16 | import java.awt.FlowLayout
17 | import java.awt.GridBagConstraints
18 | import java.awt.GridBagLayout
19 | import java.awt.GridLayout
20 | import java.awt.event.ActionEvent
21 | import java.awt.event.ActionListener
22 | import java.awt.event.ItemEvent
23 | import java.awt.event.ItemListener
24 | import java.util.*
25 | import javax.swing.*
26 | import javax.swing.border.EmptyBorder
27 |
28 |
29 | /**
30 | * GenerateDialog
31 | * @author Jowan
32 | */
33 | open class GenerateDialog(
34 | private val mProject: Project,
35 | private val mEditor: Editor,
36 | private val mSelectedText: String,
37 | /*获取mElements*/
38 | private val elements: ArrayList,
39 | /*获取当前文件*/
40 | private val mPsiFile: PsiFile,
41 | /*获取class*/
42 | private val psiClass: PsiClass,
43 | /*判断是否全选*/
44 | private var elementSize: Int,
45 | /*判断是否是ButterKnife*/
46 | private val mIsButterKnife: Boolean
47 | ) : JFrame(), ActionListener, ItemListener {
48 |
49 | // 判断OnClick是否全选
50 | private var mOnClickSize: Int = 0
51 |
52 | // 标签JPanel
53 | private val mPanelTitle = JPanel()
54 | private val mTitleName = JCheckBox(Constant.Dialog.TABLE_FIELD_VIEW_WIDGET)
55 | private val mTitleId = JLabel(Constant.Dialog.TABLE_FIELD_VIEW_ID)
56 | private val mTitleClick = JCheckBox(Constant.FIELD_ON_CLICK, false)
57 | // 命名JPanel
58 | private val mPanelTitleField = JPanel()
59 | private val mTitleFieldGroup = ButtonGroup()
60 | // aa_bb
61 | private val mTitleFieldUnderline = JRadioButton("aa_bb")
62 | // aaBb
63 | private val mTitleFieldHump = JRadioButton("aaBb")
64 | // mAaBb
65 | private val mTitleFieldPrefix = JRadioButton("mAaBb", true)
66 |
67 | // 内容JPanel
68 | private val mContentJPanel = JPanel()
69 | private val mContentLayout = GridBagLayout()
70 | private val mContentConstraints = GridBagConstraints()
71 | // 内容JBScrollPane滚动
72 | private lateinit var jScrollPane: JBScrollPane
73 |
74 | // 底部JPanel
75 | // LayoutInflater JPanel
76 | private val mPanelInflater = JPanel(FlowLayout(FlowLayout.LEFT))
77 | // 是否选择LayoutInflater
78 | private val mLayoutInflater = JCheckBox(Constant.Dialog.FIELD_LAYOUT_INFLATER, false)
79 | // 手动修改LayoutInflater字段名
80 | private lateinit var mLayoutInflaterField: JTextField
81 | private var type = 3
82 |
83 | // viewHolder
84 | private val mPanelViewHolder = JPanel(FlowLayout(FlowLayout.LEFT))
85 | private val mViewHolderCheck = JCheckBox(Constant.Dialog.VIEWHOLDER_CHECK, false)
86 |
87 | // 是否需要强转
88 | private val mPanelNeedCasts = JPanel(FlowLayout(FlowLayout.LEFT))
89 | private val mNeedCastsCheck = JCheckBox(Constant.Dialog.NEED_CASTS, true)
90 |
91 | // 是否bind,默认是true
92 | private val mBind = JCheckBox(Constant.Dialog.FIELD_BUTTERKNIFE_BIND, true)
93 |
94 | // 确定、取消JPanel
95 | private val mPanelButtonRight = JPanel()
96 | private val mButtonConfirm = JButton(Constant.Dialog.BUTTON_CONFIRM)
97 | private val mButtonCancel = JButton(Constant.Dialog.BUTTON_CANCEL)
98 |
99 | // GridBagLayout不要求组件的大小相同便可以将组件垂直、水平或沿它们的基线对齐
100 | private val mLayout = GridBagLayout()
101 | // GridBagConstraints用来控制添加进的组件的显示位置
102 | private val mConstraints = GridBagConstraints()
103 |
104 |
105 | /**
106 | * 全选设置
107 | */
108 | internal fun setCheckAll() {
109 | elements
110 | .filter { it.isClickable || mIsButterKnife }
111 | .forEach { mOnClickSize++ }
112 | mTitleName.isSelected = elementSize == elements.size
113 | mTitleName.addActionListener(this)
114 | mTitleClick.isSelected = mOnClickSize == elements.size
115 | mTitleClick.addActionListener(this)
116 | }
117 |
118 | /**
119 | * 判断是否存在ButterKnife.bind(this)/ButterKnife.bind(this, view)
120 | */
121 | internal fun checkBind() {
122 | mBind.isSelected = psiClass.isButterKnifeBindExist()
123 | }
124 |
125 | /**
126 | * 添加头部
127 | */
128 | internal fun initTopPanel() {
129 | mPanelTitle.layout = GridLayout(1, 4, 8, 10)
130 | mPanelTitle.border = EmptyBorder(5, 10, 5, 10)
131 | mPanelTitleField.layout = GridLayout(1, 3, 0, 0)
132 | mTitleName.horizontalAlignment = JLabel.LEFT
133 | mTitleName.border = EmptyBorder(0, 5, 0, 0)
134 | mTitleId.horizontalAlignment = JLabel.LEFT
135 | mTitleClick.horizontalAlignment = JLabel.LEFT
136 | // 添加listener
137 | mTitleFieldUnderline.addItemListener(this);
138 | mTitleFieldHump.addItemListener(this)
139 | mTitleFieldPrefix.addItemListener(this)
140 | // 添加到group
141 | mTitleFieldGroup.add(mTitleFieldUnderline);
142 | mTitleFieldGroup.add(mTitleFieldHump)
143 | mTitleFieldGroup.add(mTitleFieldPrefix)
144 | // 添加到JPanel
145 | mPanelTitleField.add(mTitleFieldUnderline);
146 | mPanelTitleField.add(mTitleFieldHump)
147 | mPanelTitleField.add(mTitleFieldPrefix)
148 | // 添加到JPanel
149 | mPanelTitle.add(mTitleName)
150 | mPanelTitle.add(mTitleId)
151 | mPanelTitle.add(mTitleClick)
152 | mPanelTitle.add(mPanelTitleField)
153 | mPanelTitle.setSize(900, 30)
154 | // 添加到JFrame
155 | contentPane.add(mPanelTitle, 0)
156 | }
157 |
158 | /**
159 | * 添加底部
160 | */
161 | internal fun initBottomPanel() {
162 | val viewField = mSelectedText getFieldName 3
163 | mLayoutInflaterField = JTextField(viewField, viewField.length)
164 | // 添加监听
165 | mButtonConfirm.addActionListener(this)
166 | mButtonCancel.addActionListener(this)
167 | mViewHolderCheck.addActionListener(this)
168 | // viewHolder
169 | mPanelViewHolder.add(mViewHolderCheck)
170 | // casts
171 | mNeedCastsCheck.isEnabled = !mIsButterKnife
172 | mPanelNeedCasts.add(mNeedCastsCheck)
173 | // 右边
174 | mPanelButtonRight.add(mButtonConfirm)
175 | mPanelButtonRight.add(mButtonCancel)
176 | // 添加到JPanel
177 | //mPanelInflater.add(mCheckAll);
178 | if (mIsButterKnife) {
179 | mPanelInflater.add(mBind)
180 | }
181 | mPanelInflater.add(mLayoutInflater)
182 | mPanelInflater.add(mLayoutInflaterField)
183 | // 添加到JFrame
184 | contentPane.add(mPanelInflater, 2)
185 | contentPane.add(mPanelViewHolder, 3)
186 | contentPane.add(mPanelNeedCasts, 4)
187 | contentPane.add(mPanelButtonRight, 5)
188 | }
189 |
190 | /**
191 | * 解析mElements,并添加到JPanel
192 | */
193 | internal fun initContentPanel() {
194 | mContentJPanel.removeAll()
195 | // 设置内容
196 | for (i in elements.indices) {
197 | val element = elements[i]
198 | val itemJPanel = IdBean(GridLayout(1, 4, 10, 10),
199 | EmptyBorder(5, 10, 5, 10),
200 | JCheckBox(element.name),
201 | JLabel(element.id),
202 | JCheckBox(),
203 | JTextField(element.fieldName),
204 | element.isEnable,
205 | element.isClickable,
206 | element.isClickEnable)
207 | // 监听
208 | itemJPanel.setEnableActionListener { enableCheckBox ->
209 | element.isEnable = enableCheckBox.isSelected
210 | elementSize = if (enableCheckBox.isSelected) elementSize + 1 else elementSize - 1
211 | mTitleName.isSelected = elementSize == elements.size
212 | }
213 | itemJPanel.setClickActionListener { clickCheckBox ->
214 | element.isClickable = clickCheckBox.isSelected
215 | mOnClickSize = if (clickCheckBox.isSelected) mOnClickSize + 1 else mOnClickSize - 1
216 | mTitleClick.isSelected = mOnClickSize == elements.size
217 | }
218 | itemJPanel.setFieldFocusListener { fieldJTextField -> element.fieldName = fieldJTextField.text }
219 | mContentJPanel.add(itemJPanel)
220 | mContentConstraints.fill = GridBagConstraints.HORIZONTAL
221 | mContentConstraints.gridwidth = 0
222 | mContentConstraints.gridx = 0
223 | mContentConstraints.gridy = i
224 | mContentConstraints.weightx = 1.0
225 | mContentLayout.setConstraints(itemJPanel, mContentConstraints)
226 | }
227 | mContentJPanel.layout = mContentLayout
228 | jScrollPane = JBScrollPane(mContentJPanel)
229 | jScrollPane.revalidate()
230 | // 添加到JFrame
231 | contentPane.add(jScrollPane, 1)
232 | }
233 |
234 | /**
235 | * 设置Constraints
236 | */
237 | internal fun setConstraints() {
238 | // 使组件完全填满其显示区域
239 | mConstraints.fill = GridBagConstraints.BOTH
240 | // 设置组件水平所占用的格子数,如果为0,就说明该组件是该行的最后一个
241 | mConstraints.gridwidth = 0
242 | // 第几列
243 | mConstraints.gridx = 0
244 | // 第几行
245 | mConstraints.gridy = 0
246 | // 行拉伸0不拉伸,1完全拉伸
247 | mConstraints.weightx = 1.0
248 | // 列拉伸0不拉伸,1完全拉伸
249 | mConstraints.weighty = 0.0
250 | // 设置组件
251 | mLayout.setConstraints(mPanelTitle, mConstraints)
252 | mConstraints.fill = GridBagConstraints.BOTH
253 | mConstraints.gridwidth = 1
254 | mConstraints.gridx = 0
255 | mConstraints.gridy = 1
256 | mConstraints.weightx = 1.0
257 | mConstraints.weighty = 1.0
258 | mLayout.setConstraints(jScrollPane, mConstraints)
259 | mConstraints.fill = GridBagConstraints.HORIZONTAL
260 | mConstraints.gridwidth = 0
261 | mConstraints.gridx = 0
262 | mConstraints.gridy = 2
263 | mConstraints.weightx = 1.0
264 | mConstraints.weighty = 0.0
265 | mLayout.setConstraints(mPanelInflater, mConstraints)
266 | mConstraints.fill = GridBagConstraints.HORIZONTAL
267 | mConstraints.gridwidth = 0
268 | mConstraints.gridx = 0
269 | mConstraints.gridy = 3
270 | mConstraints.weightx = 1.0
271 | mConstraints.weighty = 0.0
272 | mLayout.setConstraints(mPanelViewHolder, mConstraints)
273 | mConstraints.fill = GridBagConstraints.HORIZONTAL
274 | mConstraints.gridwidth = 0
275 | mConstraints.gridx = 0
276 | mConstraints.gridy = 4
277 | mConstraints.weightx = 1.0
278 | mConstraints.weighty = 0.0
279 | mLayout.setConstraints(mPanelNeedCasts, mConstraints)
280 | mConstraints.fill = GridBagConstraints.NONE
281 | mConstraints.gridwidth = 0
282 | mConstraints.gridx = 0
283 | mConstraints.gridy = 5
284 | mConstraints.weightx = 0.0
285 | mConstraints.weighty = 0.0
286 | mConstraints.anchor = GridBagConstraints.EAST
287 | mLayout.setConstraints(mPanelButtonRight, mConstraints)
288 | }
289 |
290 | /**
291 | * 显示dialog
292 | */
293 | fun showDialog() {
294 | // 显示
295 | isVisible = true
296 | }
297 |
298 | /**
299 | * 设置JFrame参数
300 | */
301 | internal fun setDialog() {
302 | // 设置标题
303 | title = if (mIsButterKnife) {
304 | Constant.Dialog.TITLE_BUTTERKNIFE
305 | } else {
306 | Constant.Dialog.TITLE_FINDVIEWBYID
307 | }
308 | // 设置布局管理
309 | layout = mLayout
310 | // 不可拉伸
311 | isResizable = false
312 | // 设置大小
313 | setSize(810, 405)
314 | // 自适应大小
315 | // pack();
316 | // 设置居中,放在setSize后面
317 | setLocationRelativeTo(null)
318 | // 显示最前
319 | isAlwaysOnTop = true
320 | }
321 |
322 | /**
323 | * 关闭dialog
324 | */
325 | fun cancelDialog() {
326 | isVisible = false
327 | dispose()
328 | }
329 |
330 | /**
331 | * 刷新JScrollPane内容
332 | */
333 | private fun refreshJScrollPane() {
334 | remove(jScrollPane)
335 | initContentPanel()
336 | setConstraints()
337 | revalidate()
338 | }
339 |
340 | /**
341 | * 生成
342 |
343 | * @param isLayoutInflater 是否是LayoutInflater.from(this).inflate(R.layout.activity_main, null);
344 | *
345 | * @param text 自定义text
346 | *
347 | * @param isBind 是否是bind
348 | *
349 | * @param viewHolder 是否是viewHolder
350 | *
351 | * @param isButterKnife 是否是ButterKnife
352 | *
353 | * @param mNeedCasts 是否需要强转
354 | */
355 | private fun setCreator(isLayoutInflater: Boolean, text: String, isBind: Boolean, viewHolder: Boolean, isButterKnife: Boolean, mNeedCasts: Boolean) {
356 | GenerateCreator(command = Constant.CREATOR_COMMAND_NAME,
357 | mDialog = this,
358 | mEditor = mEditor,
359 | mFile = mPsiFile,
360 | mClass = psiClass,
361 | mProject = psiClass.project,
362 | mElements = elements,
363 | mFactory = JavaPsiFacade.getElementFactory(psiClass.project),
364 | mSelectedText = mSelectedText,
365 | mIsLayoutInflater = isLayoutInflater,
366 | mLayoutInflaterText = text,
367 | mLayoutInflaterType = type,
368 | mIsButterKnife = isButterKnife,
369 | mIsBind = isBind,
370 | mViewHolder = viewHolder,
371 | mNeedCasts = mNeedCasts).execute()
372 | }
373 |
374 | override fun actionPerformed(e: ActionEvent) {
375 | when (e.actionCommand) {
376 | Constant.Dialog.BUTTON_CONFIRM -> {
377 | cancelDialog()
378 | if (mLayoutInflater.isSelected && mLayoutInflaterField.text.isEmpty()) {
379 | mEditor.showPopupBalloon(Constant.Action.SELECTED_LAYOUT_FIELD_TEXT_NO_NULL, 5)
380 | return
381 | }
382 | setCreator(mLayoutInflater.isSelected, mLayoutInflaterField.text, mBind.isSelected, mViewHolderCheck.isSelected, mIsButterKnife, mNeedCastsCheck.isSelected)
383 | }
384 | Constant.Dialog.BUTTON_CANCEL -> cancelDialog()
385 | Constant.FIELD_ON_CLICK -> {
386 | // 刷新
387 | for (element in elements) {
388 | element.isClickable = mTitleClick.isSelected
389 | }
390 | mOnClickSize = if (mTitleClick.isSelected) elements.size else 0
391 | refreshJScrollPane()
392 | }
393 | Constant.Dialog.TABLE_FIELD_VIEW_WIDGET -> {
394 | // 刷新
395 | for (element in elements) {
396 | element.isEnable = mTitleName.isSelected
397 | }
398 | elementSize = if (mTitleName.isSelected) elements.size else 0
399 | refreshJScrollPane()
400 | }
401 | Constant.Dialog.VIEWHOLDER_CHECK -> {
402 | mLayoutInflater.isEnabled = !mViewHolderCheck.isSelected
403 | mLayoutInflaterField.isEnabled = !mViewHolderCheck.isSelected
404 | mBind.isEnabled = !mViewHolderCheck.isSelected
405 | }
406 | }
407 | }
408 |
409 | override fun itemStateChanged(e: ItemEvent) {
410 | type = when (e.source) {
411 | mTitleFieldPrefix -> 3
412 | mTitleFieldHump -> 2
413 | else -> 1
414 | }
415 | for (element in elements) {
416 | if (element.isEnable) {
417 | // 设置类型
418 | element.fieldNameType = type
419 | // 置空
420 | element.fieldName = ""
421 | }
422 | }
423 | mLayoutInflaterField.text = mSelectedText getFieldName type
424 | refreshJScrollPane()
425 | }
426 | }
427 |
--------------------------------------------------------------------------------