国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android Studio插件開發之 - IOC注解生成器

2019-11-09 17:50:51
字體:
來源:轉載
供稿:網友

1.概述


  上一期我們已經分享了Android Studio插件開發之 - 基礎入門篇。那么現在我們來動手寫一個IOC注解生成器,有點類似于ButterKnife的插件一樣自動給我們生成代碼,在網上找了很多資料國內基本就在HelloWorld階段,也有很多哥們向我反應插件的代碼還是有點蒙B。代碼方面能理解就理解,不理解也不強求,如果你能改一改別人已經寫好的插件就最好了,實在不行我們干脆也別折騰了大不了不用,本文章旨在給自己想寫一些奇葩插件的哥們一些引導。   廢話不多說,請看具體效果:

GIF.gif   自動生成注解代碼,跟ButterKnife的插件類似,但是我們自己寫的插件生成的注解代碼更加符合google源碼規范,而且是基于我們自己動手打造一套IOC注解框架。當然我們可以去參考ButterKnife的插件是怎么寫的,但是我看了一下里面的東西太多了,我們干脆自己來吧。

  所有分享大綱:2017Android進階之路與你同行

  視頻講解地址:周六晚上八點     

2.實現


2.1 思路整理

  我們先來整理一下思路,要實現這么個插件我們需要做一些什么東東: - 獲取光標所在行的布局文件 –> R.layout.xxxx.xml; - 搜索整個項目獲取到R.layout.xxxx.xml文件; - 通過該布局文件去遍歷找出含有id的布局標簽,當然如果考慮完善一點需要考慮include等等; - 遍歷完成后生成對話框,讓用戶可以自己選擇需要生成注解的View以及點擊事件,這個是java GUI里面的內容 - 最后當用戶點擊確定生成最終的注解代碼即可

  這么說起來還是挺簡單的,當然其中的細節還是讓人很蛋疼的,需要不斷反復的調試。

2.2 具體實現

獲取光標所在行的布局文件 –> R.layout.xxxx.xml; /** * 獲取當前光標的layout文件 */ PRivate String getCurrentLayout(Editor editor) { Document document = editor.getDocument(); CaretModel caretModel = editor.getCaretModel(); int caretOffset = caretModel.getOffset(); int lineNum = document.getLineNumber(caretOffset); int lineStartOffset = document.getLineStartOffset(lineNum); int lineEndOffset = document.getLineEndOffset(lineNum); // 獲取當前光標所在行的所有內容 String lineContent = document.getText(new TextRange(lineStartOffset, lineEndOffset)); String layoutMatching = "R.layout."; if (!TextUtils.isEmpty(lineContent) && lineContent.contains(layoutMatching)) { // 獲取layout文件的字符串 int startPosition = lineContent.indexOf(layoutMatching) + layoutMatching.length(); int endPosition = lineContent.indexOf(")", startPosition); String layoutStr = lineContent.substring(startPosition, endPosition); // 可能是另外一種情況 View.inflate if (layoutStr.contains(",")) { endPosition = lineContent.indexOf(",", startPosition); layoutStr = lineContent.substring(startPosition, endPosition); } return layoutStr; } return null; }搜索整個項目獲取到R.layout.xxxx.xml文件; @Override public void actionPerformed(AnActionEvent e) { // 獲取project Project project = e.getProject(); // 獲取選中內容 final Editor mEditor = e.getData(PlatformDataKeys.EDITOR); if (null == mEditor) { return; } SelectionModel model = mEditor.getSelectionModel(); mSelectedText = model.getSelectedText(); // 未選中布局內容,顯示dialog if (TextUtils.isEmpty(mSelectedText)) { // 獲取光標所在位置的布局 mSelectedText = getCurrentLayout(mEditor); if (TextUtils.isEmpty(mSelectedText)) { mSelectedText = Messages.showInputDialog(project, "布局內容:(不需要輸入R.layout.)", "未選中布局內容,請輸入layout文件名", Messages.getInformationIcon()); if (TextUtils.isEmpty(mSelectedText)) { Util.showPopupBalloon(mEditor, "未輸入layout文件名", 5); return; } } } // 獲取布局文件,通過FilenameIndex.getFilesByName獲取 // GlobalSearchScope.allScope(project)搜索整個項目 PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, mSelectedText + ".xml", GlobalSearchScope.allScope(project)); if (psiFiles.length <= 0) { Util.showPopupBalloon(mEditor, "未找到選中的布局文件" + mSelectedText, 5); return; } XmlFile xmlFile = (XmlFile) psiFiles[0]; List<Element> elements = new ArrayList<>(); Util.getIDsFromLayout(xmlFile, elements); // 將代碼寫入文件,不允許在主線程中進行實時的文件寫入 if (elements.size() != 0) { PsiFile psiFile = PsiUtilBase.getPsiFileInEditor(mEditor, project); PsiClass psiClass = Util.getTargetClass(mEditor, psiFile); // 有的話就創建變量和findViewById if (mDialog != null && mDialog.isShowing()) { mDialog.cancelDialog(); } mDialog = new FindViewByIdDialog(mEditor, project, psiFile, psiClass, elements, mSelectedText); mDialog.showDialog(); } else { Util.showPopupBalloon(mEditor, "未找到任何Id", 5); } }

通過該布局文件去遍歷找出含有id的布局標簽,當然如果考慮完善一點需要考慮include等等;

/**

獲取所有id *@param file@param elements

@return */ public static java.util.List getIDsFromLayout(final PsiFile file, final java.util.List elements) { // To iterate over the elements in a file // 遍歷一個文件的所有元素 file.accept(new XmlRecursiveElementVisitor() { @Override public void visitElement(PsiElement element) { super.visitElement(element); // 解析Xml標簽 if (element instanceof XmlTag) { XmlTag tag = (XmlTag) element; // 獲取Tag的名字(TextView)或者自定義 String name = tag.getName(); // 如果有include if (name.equalsIgnoreCase(“include”)) { // 獲取布局 XmlAttribute layout = tag.getAttribute(“layout”, null); // 獲取project Project project = file.getProject(); // 布局文件 XmlFile include = null; PsiFile[] psiFiles = FilenameIndex.getFilesByName(project, getLayoutName(layout.getValue()) + “.xml”, GlobalSearchScope.allScope(project)); if (psiFiles.length > 0) { include = (XmlFile) psiFiles[0]; } if (include != null) { // 遞歸 getIDsFromLayout(include, elements); return; } } // 獲取id字段屬性 XmlAttribute id = tag.getAttribute(“android:id”, null); if (id == null) { return; } // 獲取id的值 String idValue = id.getValue(); if (idValue == null) { return; } XmlAttribute aClass = tag.getAttribute(“class”, null); if (aClass != null) { name = aClass.getValue(); } // 添加到list try { Element e = new Element(name, idValue, tag); elements.add(e); } catch (IllegalArgumentException e) {

} }}

}); return elements; }

遍歷完成后生成對話框,讓用戶可以自己選擇需要生成注解的View以及點擊事件,這個是Java GUI里面的內容,我就大致黏貼一部分代碼把,把對話框中間的部分黏貼出來,具體可以去github下載源碼 /** * 解析mElements,并添加到JPanel */ private void initContentPanel() { mContentJPanel.removeAll(); // 設置內容 for (int i = 0; i < mElements.size(); i++) { Element mElement = mElements.get(i); IdBean itemJPanel = new IdBean(new GridLayout(1, 4, 10, 10), new EmptyBorder(5, 10, 5, 10), new JCheckBox(mElement.getName()), new JLabel(mElement.getId()), new JCheckBox(), new JTextField(mElement.getFieldName()), mElement); // 監聽 itemJPanel.setEnableActionListener(this); itemJPanel.setClickActionListener(clickCheckBox -> mElement.setIsCreateClickMethod(clickCheckBox.isSelected())); itemJPanel.setFieldFocusListener(fieldJTextField -> mElement.setFieldName(fieldJTextField.getText())); mContentJPanel.add(itemJPanel); mContentConstraints.fill = GridBagConstraints.HORIZONTAL; mContentConstraints.gridwidth = 0; mContentConstraints.gridx = 0; mContentConstraints.gridy = i; mContentConstraints.weightx = 1; mContentLayout.setConstraints(itemJPanel, mContentConstraints); } mContentJPanel.setLayout(mContentLayout); jScrollPane = new JBScrollPane(mContentJPanel); jScrollPane.revalidate(); // 添加到JFrame getContentPane().add(jScrollPane, 1); }最后當用戶點擊確定生成最終的注解代碼即可,主要生成兩部分代碼@ViewById(R.id.xxx) , @OnClick(R.id.xxx)即可 /** * 創建注解View變量 */ private void generateFields() { for (Element element : mElements) { if (mClass.getText().contains("@ViewById(" + element.getFullID() + ")")) { // 不創建新的變量 continue; } // 設置變量名,獲取text里面的內容 String text = element.getXml().getAttributeValue("android:text"); if (TextUtils.isEmpty(text)) { // 如果是text為空,則獲取hint里面的內容 text = element.getXml().getAttributeValue("android:hint"); } // 如果是@string/app_name類似 if (!TextUtils.isEmpty(text) && text.contains("@string/")) { text = text.replace("@string/", ""); // 獲取strings.xml PsiFile[] psiFiles = FilenameIndex.getFilesByName(mProject, "strings.xml", GlobalSearchScope.allScope(mProject)); if (psiFiles.length > 0) { for (PsiFile psiFile : psiFiles) { // 獲取src/main/res/values下面的strings.xml文件 String dirName = psiFile.getParent().toString(); if (dirName.contains("src//main//res//values")) { text = Util.getTextFromStringsXml(psiFile, text); } } } } StringBuilder fromText = new StringBuilder(); if (!TextUtils.isEmpty(text)) { fromText.append("/****" + text + "****//n"); } fromText.append("@ViewById(" + element.getFullID() + ")/n"); fromText.append("private "); fromText.append(element.getName()); fromText.append(" "); fromText.append(element.getFieldName()); fromText.append(";"); // 創建點擊方法 if (element.isCreateFiled()) { // 添加到class mClass.add(mFactory.createFieldFromText(fromText.toString(), mClass)); } } } /** * 創建OnClick方法 */ private void generateOnClickMethod() { for (Element element : mElements) { // 可以使用并且可以點擊 if (element.isCreateClickMethod()) { // 需要創建OnClick方法 String methodName = getClickMethodName(element) + "Click"; PsiMethod[] onClickMethods = mClass.findMethodsByName(methodName, true); boolean clickMethodExist = onClickMethods.length > 0; if (!clickMethodExist) { // 創建點擊方法 createClickMethod(methodName, element); } } } } /** * 創建一個點擊事件 */ private void createClickMethod(String methodName, Element element) { // 拼接方法的字符串 StringBuilder methodBuilder = new StringBuilder(); methodBuilder.append("@OnClick(" + element.getFullID() + ")/n"); methodBuilder.append("private void " + methodName + "(" + element.getName() + " " + getClickMethodName(element) + "){"); methodBuilder.append("/n}"); // 創建OnClick方法 mClass.add(mFactory.createMethodFromText(methodBuilder.toString(), mClass)); } /** * 獲取點擊方法的名稱 */ public String getClickMethodName(Element element) { String[] names = element.getId().split("_"); // aaBbCc StringBuilder sb = new StringBuilder(); for (int i = 0; i < names.length; i++) { if (i == 0) { sb.append(names[i]); } else { sb.append(Util.firstToUpperCase(names[i])); } } return sb.toString(); }

  如果在公司的時候比較閑像我一樣,那么還是可以去了解一下插件開發的,我們可以利用它去生成代碼或者修改代碼找沒有用到的資源等等等等,還是蠻不錯的,如果是天天加班還是應該考慮一下學習成本,因為有些地方剛接觸還是容易蒙B。

附上源碼地址:https://github.com/Shenmowen/DarrenIOC

所有分享大綱:2017Android進階之路與你同行

視頻講解地址:周六晚上八點

下一期我們會講解 模板設計模式構建整個應用的BaseActivity


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 勐海县| 苗栗县| 文昌市| 独山县| 玉树县| 连州市| 牡丹江市| 恩施市| 大宁县| 察隅县| 玉门市| 遂平县| 阿拉善左旗| 蒲江县| 迁西县| 长葛市| 黔东| 大姚县| 缙云县| 都兰县| 蕲春县| 威海市| 类乌齐县| 汾阳市| 万全县| 上林县| 五家渠市| 长乐市| 泽库县| 共和县| 杭州市| 闽清县| 通河县| 冀州市| 隆德县| 绥芬河市| 辉县市| 疏勒县| 疏勒县| 马龙县| 乌兰县|