當項目引入mvp框架的話乖菱,雖然代碼結(jié)構(gòu)邏輯簡單了助币,但是創(chuàng)建類的過程太繁瑣了并且都是千篇一律的静陈,所以我們有沒有這樣的工具代替呢桑李,答案是有的!
在寫這份文章之前窿给,我是通過http://lib.csdn.net/article/android/63052該文章學(xué)習(xí)的贵白,然后下面的內(nèi)容是我在創(chuàng)建的過程記錄的。如果下面文章依然看不懂的話崩泡,可以在這個鏈接下載源碼更仔細能看到禁荒。但是不知道這個源碼是不是舊版的原因,反正我這邊是運行不了的角撞。
1. 開發(fā)工具下載
下載IntelliJ IDEA呛伴,用過studio都知道它是在IntelliJ IDEA基礎(chǔ)上開發(fā)的。下載地址:https://www.jetbrains.com/idea/
我下載的是2017.3.1的版本谒所。
2. 創(chuàng)建項目
(1)點擊Next热康,創(chuàng)建名稱起個叫:MvpAutomaticCreation
項目結(jié)構(gòu):(2)點擊src,在這里選擇你的sdk地址(如果沒有配置sdk的話,是不能使用一些別的功能的)
(3)一切配置完后劣领,然后就可以開始創(chuàng)建類了姐军,點擊src,在這里創(chuàng)建Action
(4)填寫Action信息
屬性 | 描述 |
---|---|
Action ID | 這個Action的唯一標示 |
Class Name | 類名稱 |
Name | 這個插件在菜單上的名稱 |
Description | 關(guān)于這個插件的描述信息 |
Groups | 代表這個插件會出現(xiàn)的位置。比如想讓這個插件出現(xiàn)在Code菜單下的第一次選項,就如圖中一樣選擇CodeMenu(Code)奕锌,右邊Anchor選擇First |
Keyboard Shortcuts | 快捷鍵設(shè)置著觉。圖中設(shè)置Alt+T。 |
(5)編寫代碼
在編寫代碼之前辽话,我們肯定已經(jīng)知道我們想要生成什么樣的代碼了肄鸽,每個人寫的框架都不一樣,那么假設(shè)我們現(xiàn)在寫的一個mvp是這么一個架構(gòu)油啤,圖中的Base可以忽略典徘,因為這是兩個項目同時引用的這一個類。注意:我這邊編寫的生成類村砂,因為考慮到實際使用,我是不寫基類的生成的
后綴名當然是txt了础废,創(chuàng)建如下:
代碼里面的$packagename、$basepackagename罕模、$author评腺、$description、$date淑掌、$name這些字符都是可以動態(tài)替換的蒿讥。
(5.2)開始創(chuàng)建插件ui了可視化編輯:
(5.3)接下來解析對應(yīng)view的控制類,請看注釋
import javax.swing.*;
import java.awt.event.*;
public class MvpAutomaticCreation extends JDialog {
private JPanel contentPane;
private JButton buttonOK;
private JButton buttonCancel;
private JTextField textField1;
private JTextField textField2;
private DialogCallBack mCallBack;
/**
* 在自動創(chuàng)建該類的時候,添加一個回調(diào)函數(shù)DialogCallBack抛腕,并且改變了onOK這個方法
* @param callBack 回調(diào)函數(shù)
*/
public MvpAutomaticCreation(DialogCallBack callBack) {
this.mCallBack = callBack;
setTitle("MvpAutomaticCreation");
setContentPane(contentPane);
setModal(true);
getRootPane().setDefaultButton(buttonOK);
buttonOK.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onOK();
}
});
buttonCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onCancel();
}
});
// call onCancel() when cross is clicked
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
onCancel();
}
});
// call onCancel() on ESCAPE
contentPane.registerKeyboardAction(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onCancel();
}
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
}
private void onOK() {
// add your code here
if (null != mCallBack){
mCallBack.ok(textField1.getText().trim(), textField2.getText().trim());
}
dispose();
}
private void onCancel() {
// add your code here if necessary
dispose();
}
// 這個作廢芋绸,去掉,無用
// public static void main(String[] args) {
// MvpAutomaticCreation dialog = new MvpAutomaticCreation();
// dialog.pack();
// dialog.setVisible(true);
// System.exit(0);
// }
public interface DialogCallBack{
void ok(String author, String moduleName);
}
}
(5.4)然后接著看執(zhí)行類
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 執(zhí)行類
* (1)會獲取包名担敌,然后讀取模板文件摔敛,替換模板文件中動態(tài)字符,在Dialog輸入的作者和模塊名稱也會替換模板中字符全封,
* (2)最后通過包名路徑生成類文件
*
* 后面我會根據(jù)實際工作需求马昙,會想辦法改進選擇生成fragment還是activity
* 而作者名稱也應(yīng)該能設(shè)置是默認的
*
*/
public class MvpAutomaticCreationAction extends AnAction {
private Project project;
private String packageName = "";//包名
private String mAuthor;//作者
private String mModuleName;//模塊名稱
/**
* 創(chuàng)建類型枚舉
*/
private enum CodeType {
Activity, Fragment, Contract, Presenter, BaseView, BasePresenter
}
@Override
public void actionPerformed(AnActionEvent e) {
// TODO: insert action logic here
project = e.getData(PlatformDataKeys.PROJECT);
packageName = getPackageName();
refreshProject(e);
init();
}
/**
* 刷新項目
* @param e
*/
private void refreshProject(AnActionEvent e) {
e.getProject().getBaseDir().refresh(false, true);
}
/**
* 初始化Dialog
*/
private void init(){
MvpAutomaticCreation myDialog = new MvpAutomaticCreation(new MvpAutomaticCreation.DialogCallBack() {
@Override
public void ok(String author, String moduleName) {
// 實例化ok事件
mAuthor = author;
mModuleName = moduleName;
createClassFiles();
Messages.showInfoMessage(project,"create mvp code success","title");
}
});
myDialog.setVisible(true);
}
/**
* 生成類文件
*/
private void createClassFiles() {
createClassFile(CodeType.Activity);
createClassFile(CodeType.Fragment);
createClassFile(CodeType.Contract);
createClassFile(CodeType.Presenter);
// createBaseClassFile(CodeType.BaseView); // 暫時作廢
// createBaseClassFile(CodeType.BasePresenter); // 暫時作廢
}
/**
* 生成mvp框架代碼
* @param codeType 類型
*/
private void createClassFile(CodeType codeType) {
String fileName = "";
String content = "";
String appPath = getAppPath();
switch (codeType){
case Activity:
fileName = "TemplateActivity.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Activity.java");
break;
case Fragment:
fileName = "TemplateFragment.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Fragment.java");
break;
case Contract:
fileName = "TemplateContract.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Contract.java");
break;
case Presenter:
fileName = "TemplatePresenter.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Presenter.java");
break;
}
}
/**
* 生成
* @param content 類中的內(nèi)容
* @param classPath 類文件路徑
* @param className 類文件名稱
*/
private void writeToFile(String content, String classPath, String className) {
try {
File floder = new File(classPath);
if (!floder.exists()){
floder.mkdirs();
}
File file = new File(classPath + "/" + className);
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 替換模板中字符
* @param content
* @return
*/
private String dealTemplateContent(String content) {
content = content.replace("$name", mModuleName);
if (content.contains("$packagename")){
content = content.replace("$packagename", packageName + "." + mModuleName.toLowerCase());
}
if (content.contains("$basepackagename")){
content = content.replace("$basepackagename", packageName + ".base");
}
content = content.replace("$author", mAuthor);
content = content.replace("$date", getDate());
return content;
}
/**
* 獲取包名文件路徑
* @return
*/
private String getAppPath(){
String packagePath = packageName.replace(".", "/");
String appPath = project.getBasePath() + "/App/src/main/java/" + packagePath + "/";
return appPath;
}
/**
* 獲取當前時間
* @return
*/
public String getDate() {
Date currentTime = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
String dateString = formatter.format(currentTime);
return dateString;
}
/**
* 從AndroidManifest.xml文件中獲取當前app的包名
* @return 當前app的包名
*/
private String getPackageName() {
String package_name = "";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(project.getBasePath() + "/App/src/main/AndroidManifest.xml");
NodeList nodeList = doc.getElementsByTagName("manifest");
for (int i = 0; i < nodeList.getLength(); i++){
Node node = nodeList.item(i);
Element element = (Element) node;
package_name = element.getAttribute("package");
}
} catch (Exception e) {
e.printStackTrace();
}
return package_name;
}
/**
* 讀取模板文件中的字符內(nèi)容
* @param fileName 模板文件名
*/
private String ReadTemplateFile(String fileName) {
InputStream in = null;
in = this.getClass().getResourceAsStream("/Template/" + fileName);
String content = "";
try {
content = new String(readStream(in));
} catch (IOException e) {
e.printStackTrace();
}
return content;
}
/**
* 讀取數(shù)據(jù)
* @param inputStream
*/
private byte[] readStream(InputStream inputStream) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
outputStream.close();
inputStream.close();
}
return outputStream.toByteArray();
}
}
(6)部署插件
(6.1)找到該文件,填一些資料(6.2)然后生成
會創(chuàng)建一個jar包行楞,拿到這個jar包就可以安裝到Android Studio了。
(7)部署插件
點擊Install plugin from disk...土匀,選擇自己生成的jar子房,就能導(dǎo)入成功了。
(7) 錯誤匯總
當點擊發(fā)現(xiàn)沒任何反應(yīng)的時候,我們查詢bug發(fā)現(xiàn)這么一個提示:
null
java.lang.NullPointerException
at com.intellij.ide.SystemHealthMonitor.getActionName
注意:在創(chuàng)建自定義的XXAction類時池颈,需要保證自己的XXAction類在某個package中尾序,否則會出現(xiàn)如下之類的報錯:
示例如我google中查詢:
最近找到一個很不錯的插件源碼合集
https://github.com/balsikandar/Android-Studio-Plugins
http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/plugins-develop.html