Android Studio plug-in development MVP framework code generation plug-in

Keywords: Mobile Java xml IntelliJ IDEA Android

I. overview When using Android Studio, plug-ins are often used to improve development efficiency (laziness). Some of them are commonly used: GsonFormat, ButterKnife, etc. Because introducing the mvp architecture into the project and writing various classes is a very tedious and repetitive task, so I wrote this plug-in at that time. Now I just want to talk about it in conjunction with this article. This article will teach you how to generate a plug-in for mvp framework code according to your project structure and see how it works:

As you can see, you only need to enter the name of the author and module to generate an official version of the mvp framework code structure, while generating the base directory.

If you learn how to write plug-ins, so that when you have good ideas, you can achieve it. Official website has specific steps, you can see: http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html

Begin without much nonsense.

II. Construction of Development Environment Developing tools need IntelliJ IDEA, and studio knows that it is developed on the basis of IntelliJ IDEA, so IntelliJ IDEA is easy to get started. Download address: https://www.jetbrains.com/idea/ Download the community version and install it. Start writing:

1. Create a plugin project File - > New - > Project and then select a plugin project as follows:

Click Next and enter the name of the project, which is named MvpCreate.

Look at the project structure after the project is built.

The plugin.xml is the description file of the project, which contains the copyright, author and declaration of some projects. The main label is <actions>, which will be the entry of the plug-in.

The main code is stored in the src directory.

2. Create a menu We know that Android Studio has many menus, such as the Build menu, which has a variety of functions, as well as shortcut support, as follows:

Now let's start building our own menu and select SRC - > New - > Action, as follows:

Then fill in the Action information:

Explain the attributes that need to be filled in:

Action ID: The only token representing this action.

Class Name: Class name

Name: The name of the plug-in on the menu

Description: Description information about this plug-in

Groups: Represents where the plug-in will appear. For example, if you want this plug-in to appear under the Code menu for the first time, I choose Code Menu (Code) in the figure, and Anchor on the right Chor chooses First.

Keyboard Shortcuts: Keyboard Shortcuts. Set Alt+T in the figure.

Clicking OK generates an MvpCreateAction class:

Looking at the plugin.xml file, you can see that there is an additional < action > tag under the < actions > tag, which contains the information we just filled in.

Such a plug-in is generated. Deploying this plug-in to studio is already available. How to deploy this plug-in will be discussed in the next section. Let's move on.

3. Writing Plug-in Code First of all, let's talk about the train of thought.

To generate code, we can use some templates. This template can be a txt file, add code to the template, extract the code that needs to be replaced, read the template file through the stream, and finally generate the class file. Basically, this is the way of thinking, and then it will be realized concretely:

1. New template file We compile the overall framework according to the mvp style of the official website, which is subcontracted by functional modules, including Activity, Fragment, Presenter, Contract, and then add the base class, BasePresenter, BaseView, BaseActivity, BaseFragment. To glance at:

Take a look at the internal details of the TemplateContract.txt file:

The characters $package name, $basepackage name, $author, $description, $date, $name in the code can be replaced dynamically. The specific code details of these template files can be viewed in the source code at the end, and of course, they can also be written to their own code according to their own needs.

2. New dialog The above template has been built, and now we need a dialog box in which to enter the name of the author and module. Look at the effect:

Simple rendering. Like creating Action, Dialog can be named MyDialog by right-clicking New - > Dialog directly under src, and then a MyDialog.java and MyDialog.form file will be generated under SRC directory. Clicking on the MyDialog.form file will enter a visual interface, and a user interface can be achieved by dragging and dropping:

Then take a look at the code for 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);
}

} This code is very simple, the main thing is the onOK() method of 49 lines, where the callback of the button is added. This dialog can be used directly. 3. Generating Code Implementation Now let's see the specific implementation of 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;
//Package name
private String packageName = "";
private String mAuthor;//author
private String mModuleName;//Module name

private enum  CodeType {
    Activity, Fragment, Contract, Presenter, BaseView, BasePresenter, MvpBaseActivity, MvpBaseFragment
}

[@Override](https://my.oschina.net/u/1162528)
public void actionPerformed(AnActionEvent e) {
    project = e.getData(PlatformDataKeys.PROJECT);
    packageName = getPackageName();
    init();
    refreshProject(e);
}

/**
 * Refresh item
 * [@param](https://my.oschina.net/u/2303379) e
 */
private void refreshProject(AnActionEvent e) {
    e.getProject().getBaseDir().refresh(false, true);
}

/**
 * Initialize Dialog
 */
private void init(){
    MyDialog myDialog = new MyDialog(new MyDialog.DialogCallBack() {
        [@Override](https://my.oschina.net/u/1162528)
        public void ok(String author, String moduleName) {
            mAuthor = author;
            mModuleName = moduleName;
            createClassFiles();
            Messages.showInfoMessage(project,"create mvp code success","title");
        }
    });
    myDialog.setVisible(true);

}

/**
 * Generate class files
 */
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);
}

/**
 * Generate base class
 * [@param](https://my.oschina.net/u/2303379) 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;
    }
}

/**
 * Generating mvp framework code
 * [@param](https://my.oschina.net/u/2303379) 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;
    }
}

/**
 * Get the package name file path
 * @return
 */
private String getAppPath(){
    String packagePath = packageName.replace(".", "/");
    String appPath = project.getBasePath() + "/App/src/main/java/" + packagePath + "/";
    return appPath;
}

/**
 * Replace characters in templates
 * @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;
}

/**
 * Get the current time
 * @return
 */
public String getDate() {
    Date currentTime = new Date();
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
    String dateString = formatter.format(currentTime);
    return dateString;
}


/**
 * Read the character content in the template file
 * @param fileName Template 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();
}


/**
 * generate
 * @param content Contents in classes
 * @param classPath Class File Path
 * @param className Class file name
 */
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();
    }

}

/**
 * Get the package name of the current app from the AndroidManifest.xml file
 * @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;
}

} To illustrate, we first get the package name, then read the template file, replace the dynamic characters in the template file, and replace the characters in the template with the author and module names entered in Dialog. Finally, we generate the class file through the package name path. This is a brief description of the above code. Let's see the code in detail. Such a mvp framework code generation plug-in has been completed, so let's see how to deploy it. IV. Deployment of Plug-ins 1. Fill in plug-in information Find the plugin.xml file and fill in some basic information as follows:

Fill in some plug-in information, such as name, version, description, etc. The information you fill in will see a brief introduction when you install the plug-in.

Then click Build - > Prepare Plugin Module... as shown below:

A jar package is generated in the root directory of the project:

Get this jar package and install it into Android Studio.

2. Installing plug-ins Open Android Studio, select File - > Setting - > Plugins - > Install plugin from disk, and then select the jar package just generated, as shown in the figure:

After installation, restart Studio and use it.

After restarting, you can see that there is an additional MvpHelper option under the Code menu, click or shortcut Alt+T call.

Now call this plug-in to generate code!

3. Upload to Plugins Warehouse If you have a good plug-in that you want to share with your buddies, but each time you send it as a jar package to the person in need, the other party needs Setting - > Plugins - > Install plugin from disk to find the local jar to install and use. It feels too cumbersome to install directly by browsing the Plugins warehouse. Follow the following steps to achieve:

Official Plugins Warehouse Address: https://plugins.jetbrains.com/

Find the plugin.xml file and open the following code:

<depends>com.intellij.modules.lang</depends> When you open the plug-in repository, the plug-in can be found in all the plug-in repositories, that is, the repositories of IntelliJ IDEA and Android Studio. Otherwise, it can only be found in the repository of IntelliJ IDEA. After modifying the plugin.xml file, the jar is regenerated, and then users are registered on the warehouse official network, plug-ins are uploaded, plug-in information is filled in, waiting for approval. In this way, you can search your plug-ins in the plug-in repository, as follows:

Five, last At the end of this article, there are three steps to write a plug-in:

Download IntelliJ IDEA and create plugin project; Code implementation; Generate jar packages and deploy plug-ins; The steps are very simple, the main thing is that the code implementation is more cumbersome. Now you can use your brain to write some "lazy" plug-ins.

Plug in source code: https://github.com/xunzzz/MvpCreatePlugin -------- Copyright Statement: This article is the original article of CSDN blogger Zhang zx. It follows CC 4.0 BY-SA Copyright Agreement. Please attach the link of origin and this statement for reproducing. Links to the original text: https://blog.csdn.net/fengdezhudi/article/details/53584497

Posted by hoolahoops on Mon, 14 Oct 2019 05:53:46 -0700