当项目引入mvp框架的话,虽然代码结构逻辑简单了,但是创建类的过程太繁琐了并且都是千篇一律的,所以我们有没有这样的工具代替呢,答案是有的!
在写这份文章之前,我是通过http://lib.csdn.net/article/android/63052该文章学习的,然后下面的内容是我在创建的过程记录的。如果下面文章依然看不懂的话,可以在这个链接下载源码更仔细能看到。但是不知道这个源码是不是旧版的原因,反正我这边是运行不了的。
1. 开发工具下载
下载IntelliJ IDEA,用过studio都知道它是在IntelliJ IDEA基础上开发的。下载地址:https://www.jetbrains.com/idea/
我下载的是2017.3.1的版本。
2. 创建项目
image.png(1)点击Next,创建名称起个叫:MvpAutomaticCreation
项目结构: image.png(2)点击src,在这里选择你的sdk地址(如果没有配置sdk的话,是不能使用一些别的功能的) image.png
(3)一切配置完后,然后就可以开始创建类了,点击src,在这里创建Action image.png
(4)填写Action信息
image.png属性 | 描述 |
---|---|
Action ID | 这个Action的唯一标示 |
Class Name | 类名称 |
Name | 这个插件在菜单上的名称 |
Description | 关于这个插件的描述信息 |
Groups | 代表这个插件会出现的位置。比如想让这个插件出现在Code菜单下的第一次选项,就如图中一样选择CodeMenu(Code),右边Anchor选择First |
Keyboard Shortcuts | 快捷键设置。图中设置Alt+T。 |
(5)编写代码
在编写代码之前,我们肯定已经知道我们想要生成什么样的代码了,每个人写的框架都不一样,那么假设我们现在写的一个mvp是这么一个架构,图中的Base可以忽略,因为这是两个项目同时引用的这一个类。 image.png注意:我这边编写的生成类,因为考虑到实际使用,我是不写基类的生成的
后缀名当然是txt了,创建如下: image.png 打开其中一个文件: image.png
代码里面的$packagename、$basepackagename、$author、$description、$date、$name这些字符都是可以动态替换的。
(5.2)开始创建插件ui了 image.png可视化编辑: image.png 最终效果: image.png
(5.3)接下来解析对应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;
/**
* 在自动创建该类的时候,添加一个回调函数DialogCallBack,并且改变了onOK这个方法
* @param callBack 回调函数
*/
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)然后接着看执行类
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;
/**
* 执行类
* (1)会获取包名,然后读取模板文件,替换模板文件中动态字符,在Dialog输入的作者和模块名称也会替换模板中字符,
* (2)最后通过包名路径生成类文件
*
* 后面我会根据实际工作需求,会想办法改进选择生成fragment还是activity
* 而作者名称也应该能设置是默认的
*
*/
public class MvpAutomaticCreationAction extends AnAction {
private Project project;
private String packageName = "";//包名
private String mAuthor;//作者
private String mModuleName;//模块名称
/**
* 创建类型枚举
*/
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 类中的内容
* @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;
}
/**
* 读取模板文件中的字符内容
* @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;
}
/**
* 读取数据
* @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)找到该文件,填一些资料 image.png image.png 记住这个版本号要改成145,否则Android Studio导入会报不兼容问题 image.png(6.2)然后生成
image.png
会创建一个jar包,拿到这个jar包就可以安装到Android Studio了。
image.png
(7)部署插件
点击Install plugin from disk...,选择自己生成的jar,就能导入成功了。
image.png 然后重启Android studio,在菜单这里就能看到了 image.png
(7) 错误汇总
当点击发现没任何反应的时候,我们查询bug发现这么一个提示:
null
java.lang.NullPointerException
at com.intellij.ide.SystemHealthMonitor.getActionName
注意:在创建自定义的XXAction类时,需要保证自己的XXAction类在某个package中,否则会出现如下之类的报错:
示例如我google中查询:
image.png
最近找到一个很不错的插件源码合集
https://github.com/balsikandar/Android-Studio-Plugins
http://wiki.jikexueyuan.com/project/intellij-idea-tutorial/plugins-develop.html
网友评论