Android Studio插件开发 MVP框架代码生成插件
2016-12-12 16:33
501 查看
一、概述
在使用Android Studio的时候多少都会使用插件来提高开发效率(偷懒),常用的一些:GsonFormat、ButterKnife等等;因为项目中引入mvp这种架构,写各种类是一件很重复繁琐的事,所以当时就写了这个插件,现在正好结合这篇文章拿出来说一下。这篇文章会教你根据自己的项目结构,生成一个mvp框架代码的插件,看一下运行的效果:可以看到,只需要输入作者和模块的名称就可以生成一个符合官网版本的mvp框架代码结构,同时会生成base目录。
如果学会了编写插件,这样自己有好创意的时候就可以去实现了,官网有具体的步骤,可以去看一下:http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html
废话不多说直接开始。
二、开发环境搭建
开发工具需要IntelliJ IDEA,用过studio都知道它是在IntelliJ IDEA基础上开发的,所以IntelliJ IDEA上手很简单。下载地址:https://www.jetbrains.com/idea/ 下载community版本安装即可。开始编写:1.创建一个plugin项目
File->New->Project 然后选择一个plugin项目,如下图:点击Next,输入项目的名称即可,这里取名MvpCreate。
建立工程后看一下项目结构:
其中plugin.xml为项目的说明文件,会包含一些项目的版权、作者和声明。其中主要标签为<actions>,这里会是插件的入口。
src目录下存放主要的代码。
2.创建一个菜单
我们知道Android Studio有很多菜单,例如常用的Build菜单,里面有各种各样的功能,同时还有快捷键支持,如下:现在我们开始建一个自己的菜单,选择src->New->Action,如下:
然后填写这个Action的信息:
说明一下需要填写的属性:
Action ID:代表这个Action的唯一标示。
Class Name:类名
Name:这个插件在菜单上的名称
Description:关于这个插件的描述信息
Groups:代表这个插件会出现的位置。比如想让这个插件出现在Code菜单下的第一次选项,我在图中选择CodeMenu(Code),右边Anchor选择First
Keyboard Shortcuts:快捷键设置。图中设置Alt+T。
点击OK后会生成一个MvpCreateAction类:
同时看一下plugin.xml这个文件,会发现<actions>标签下多出来了一个<action>标签,里面包含了我们刚刚填写的信息。
这样一个插件就生成了。把这个插件部署到studio已经可以使用,具体如何部署接下来会讲,继续往下看。
三、编写插件代码
先说一下思路:生成代码我们可以使用一些模板,这个模板可以是一个txt文件,在模板里面添加代码,提取出需要替换的代码,然后通过流读取模板文件,最后生成类文件。基本上就是这样的思路,接下来具体实现:
1.新建模板文件
我们按照官网的mvp风格来编写整体的框架,这里按功能模块分包,包含Activity、Fragment、Presenter、Contract,然后加上base类,BasePresenter、BaseView、BaseActivity、BaseFragment。看一下:看一下TemplateContract.txt这个文件的内部细节:
代码里面的$packagename、$basepackagename、$author、$description、$date、$name这些字符都是可以动态替换的。这些模板文件的具体代码细节在文末的源码中查看,当然也可以根据自己的需要写入自己的代码。
2.新建dialog
上面的模板已经建好,现在我们需要一个对话框,在对话框里面输入作者和模块的名字。效果看一下:很简单的效果图,和创建Action一样,Dialog可以直接在src下右键New->Dialog取名MyDialog,然后会在src目录下生生成一个MyDialog.java和MyDialog.form的文件,点击这个MyDialog.form文件会进入一个可视化界面,通过拖拽就可以实现一个用户界面:
然后看一下MyDialog.java这个类的代码:
import javax.swing.*; import java.awt.event.*; public class MyDialog extends JDialog { private JPanel contentPane; private JButton buttonOK; private JButton buttonCancel; private JTextField textField1; private JTextField textField2; private DialogCallBack mCallBack; public MyDialog(DialogCallBack callBack) { this.mCallBack = callBack; setTitle("Mvp Create Helper"); setContentPane(contentPane); setModal(true); getRootPane().setDefaultButton(buttonOK); setSize(300, 150); setLocationRelativeTo(null); 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 interface DialogCallBack{ void ok(String author, String moduleName); } }这段代码很简单,最主要的是49行的onOK()方法,在这里添加按钮的回调。这个dialog可以直接使用了。
3.生成代码实现
好了现在去看看MvpCreateAction这个类的具体实现: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; public class MvpCreateAction extends AnAction { private Project project; //包名 private String packageName = ""; private String mAuthor;//作者 private String mModuleName;//模块名称 private enum CodeType { Activity, Fragment, Contract, Presenter, BaseView, BasePresenter, MvpBaseActivity, MvpBaseFragment } @Override public void actionPerformed(AnActionEvent e) { project = e.getData(PlatformDataKeys.PROJECT); packageName = getPackageName(); init(); refreshProject(e); } /** * 刷新项目 * @param e */ private void refreshProject(AnActionEvent e) { e.getProject().getBaseDir().refresh(false, true); } /** * 初始化Dialog */ private void init(){ MyDialog myDialog = new MyDialog(new MyDialog.DialogCallBack() { @Override public void ok(String author, String moduleName) { 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); createBaseClassFile(CodeType.MvpBaseActivity); createBaseClassFile(CodeType.MvpBaseFragment); } /** * 生成base类 * @param codeType */ private void createBaseClassFile(CodeType codeType) { String fileName = ""; String content = ""; String basePath = getAppPath() + "base/"; switch (codeType){ case BaseView: if (!new File(basePath + "BaseView.java").exists()){ fileName = "TemplateBaseView.txt"; content = ReadTemplateFile(fileName); content = dealTemplateContent(content); writeToFile(content, basePath, "BaseView.java"); } break; case BasePresenter: if (!new File(basePath + "BasePresenter.java").exists()){ fileName = "TemplateBasePresenter.txt"; content = ReadTemplateFile(fileName); content = dealTemplateContent(content); writeToFile(content, basePath, "BasePresenter.java"); } break; case MvpBaseActivity: if (!new File(basePath + "MvpBaseActivity.java").exists()){ fileName = "TemplateMvpBaseActivity.txt"; content = ReadTemplateFile(fileName); content = dealTemplateContent(content); writeToFile(content, basePath, "MvpBaseActivity.java"); } break; case MvpBaseFragment: if (!new File(basePath + "MvpBaseFragment.java").exists()){ fileName = "TemplateMvpBaseFragment.txt"; content = ReadTemplateFile(fileName); content = dealTemplateContent(content); writeToFile(content, basePath, "MvpBaseFragment.java"); } break; } } /** * 生成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; } } /** * 获取包名文件路径 * @return */ private String getAppPath(){ String packagePath = packageName.replace(".", "/"); String appPath = project.getBasePath() + "/App/src/main/java/" + packagePath + "/"; return appPath; } /** * 替换模板中字符 * @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 */ public String getDate() { Date currentTime = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd"); String dateString = formatter.format(currentTime); return dateString; } /** * 读取模板文件中的字符内容 * @param fileName 模板文件名 * @return */ 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; } private byte[] readStream(InputStream inputStream) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = -1; 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(); } /** * 生成 * @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(); } } /** * 从AndroidManifest.xml文件中获取当前app的包名 * @return */ 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; } }说明一下,首先会获取包名,然后读取模板文件,替换模板文件中动态字符,在Dialog输入的作者和模块名称也会替换模板中字符,最后通过包名路径生成类文件,这是上面代码的一个简述,具体看代码吧。这样一个mvp框架代码生成插件已经完成,接下来看一下如何部署。
四、部署插件
1.填写插件信息
找到plugin.xml文件,填入一些基本信息,如下图:填写一些插件的信息,像name、version、description...等等,填写的这些信息会在安装插件时,看到它的简介。
然后点击Build->Prepare Plugin Module ...... 如图:
会在项目的根目录生成一个jar包:
拿到这个jar包就可以安装到Android Studio了。
2.安装插件
打开Android Studio,选择File->Setting->Plugins->Install plugin from disk,然后选择刚刚生成的jar包,如图:安装好了以后重启Studio即可使用。
重启后可以看到在Code菜单下多了MvpHelper这个选项,点击或者快捷键Alt+T调用。
现在调用这个插件去生成代码吧!
3.上传到Plugins仓库
如果你有个好的插件想要分享给你的小伙伴,但每次都要打成jar包发送给需要的人,然后对方需要Setting->Plugins->Install plugin from disk找到本地jar去安装使用。这样感觉太麻烦了,如果直接通过浏览Plugins仓库安装多好。按照下面步骤就可以实现:官方Plugins仓库地址:https://plugins.jetbrains.com/
找到plugin.xml文件把下面这段代码打开:
<depends>com.intellij.modules.lang</depends>打开后代表在所有的插件仓库都可以搜到这个插件,即IntelliJ IDEA和Android Studio的仓库均可搜到,要不然只能在IntelliJ IDEA的仓库搜到。
修改完plugin.xml文件后,重新生成jar,然后在仓库官网注册用户,上传插件,填写插件信息,等待审核通过。这样就可以在插件仓库搜索到自己的插件了,如下图:
五、最后
到此本文结束,编写一个插件可以说就三步:下载IntelliJ IDEA,创建plugin项目;
代码实现;
生成jar包,部署插件;
步骤很简单,主要的是代码实现上比较麻烦,现在可以动脑去编写一些“偷懒”的插件吧。
插件源码:https://github.com/xunzzz/MvpCreatePlugin
相关文章推荐
- android mvp快速开发框架介绍(自动生成android代码工具介绍)
- Android Studio插件之MVPHelper,一键生成MVP代码
- 代码生成器插件开发---代码生成项目框架的创建(2)
- android studio 插件开发(自动生成框架代码插件)
- Android Studio通过插件自动生成Pracelable的模板代码
- Android Studio开发快速创建MVP框架插件AndroidMVP
- 基于Metronic的Bootstrap开发框架经验总结(18)-- 在代码生成工具Database2Sharp中集成对Bootstrap-table插件的分页及排序支持
- Android Studio插件-自动根据布局生成Activity等代码(插件代码开源)
- Android Studio开发快速创建MVP框架插件AndroidMVP
- 软件敏捷开发平台1.0(基于框架的代码全自动生成)
- WCF开发框架形成之旅---结合代码生成工具实现快速开发
- onvif协议开发:gsoap代码框架生成命令
- 用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(三)——搭建Android开发环境,用Hibernate生成Android项目的Model层
- 【移动开发】Android应用程序中实用的代码框架(一) 推荐
- YbSoftwareFactory 代码生成插件【三】:二次开发之 IPlugInRepository 接口的实现
- Onvif开发之代码框架生成篇
- 整合框架 javaweb开发平台ssmy_m(与代码生成) java struts2 mybatis spring maven jquery
- Android 插件框架 xCombine 开发思路简介
- 分享.NET代码免费生成流程图的插件Microsoft Visual Studio Learning Pack
- 【移动开发】Android应用程序中实用的代码框架(二) 推荐