美文网首页我爱编程
为AndroidStudio开发mvp插件(MvpHelper)

为AndroidStudio开发mvp插件(MvpHelper)

作者: 半只温柔 | 来源:发表于2018-04-12 15:21 被阅读0次

    如果觉得写mvp有点枯燥无味,我们可以做点 cool 的事情:做个 as 插件


    help.png

    Google官方安卓Mvp架构demo
    https://github.com/googlesamples/android-architecture

    • todo-mvp: 基础的MVP架构。
    • todo-mvp-loaders:基于MVP架构的实现,在获取数据的部分采用了loaders架构。
    • todo-mvp-databinding: 基于MVP架构的实现,采用了数据绑定组件。
    • todo-mvp-clean: 基于MVP架构的clean架构的实现。
    • todo-mvp-dagger2: 基于MVP架构,采用了依赖注入dagger2。
    • dev-todo-mvp-contentproviders: 基于mvp-loaders架构,使用了ContenPproviders。
    • dev-todo-mvp-rxjava: 基于MVP架构,对于程序的并发处理和数据层(MVP中的Model)的抽象。

    idea插件论坛
    https://intellij-support.jetbrains.com/hc/en-us/community/topics/200366979-IntelliJ-IDEA-Open-API-and-Plugin-Development

    因为对mvp的理解不同,会有很多种方式表达架构,所以插件要极简易读,可扩展性强,所以可以选择:

    /**
         * 通过模板生成类
         * @param var1 文件生成路径
         * @param var2 文件名
         * @param var3 模板名
         * @param var4 是否生产类
         * @param var5 模板参数
         */
    public abstract PsiClass createClass(@NotNull PsiDirectory var1, @NotNull String var2, @NotNull String var3, boolean var4, @NotNull Map<String, String> var5) throws IncorrectOperationException;
    
    

    也就是只要定制不同的模板就可以实现扩展性

    直接贴下步骤:

    • 1: idea File - new - Project - IntelliJ Platform Plugin
      dir.PNG
      plugin.xml 插件配置信息:
    <idea-plugin>
      <id>com.your.company.unique.plugin.id</id>
      <name>Plugin display name here</name>
      <version>1.0</version>
      <vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor>
    
      <description><![CDATA[
          Enter short description for your plugin here.<br>
          <em>most HTML tags may be used</em>
        ]]></description>
    
      <change-notes><![CDATA[
          Add change notes here.<br>
          <em>most HTML tags may be used</em>
        ]]>
      </change-notes>
    
      <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
      <idea-version since-build="145.0"/>
    
      <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
           on how to target different products -->
      <!-- uncomment to enable plugin in all products
      <depends>com.intellij.modules.lang</depends>
      -->
    
      <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
      </extensions>
    
      <actions>
        <action id="generateMVPField" class="actions.MvpGenerator" text="Generate MVP By String"
                description="Generate MVP" icon="/icons/app_icon.png">
          <add-to-group group-id="GenerateGroup" anchor="first"/>
          <add-to-group group-id="NewGroup" anchor="last"/>
          <keyboard-shortcut keymap="$default" first-keystroke="alt I"/>
        </action>
      </actions>
    
    </idea-plugin>
    

    action:

    public class MvpGenerator extends AnAction implements MvpHandler.OnGenerateListener {
    
        private AnActionEvent actionEvent;
        private MvpHandler mvpHandler;
        private Map<String, String> map = new HashMap<>();
    
        @Override
        public void actionPerformed(AnActionEvent anActionEvent) {
            this.actionEvent = anActionEvent;
            mvpHandler = new MvpHandler();
            mvpHandler.setTitle("输入名称-生成MVP相关类");
            mvpHandler.setOnGenerateListener(this);
            mvpHandler.pack();
            //设置对话框跟随当前windows窗口
            mvpHandler.setLocationRelativeTo(WindowManager.getInstance().getFrame(anActionEvent.getProject()));
            mvpHandler.setVisible(true);
        }
    
    
        @Override
        public void onGenerate(String text) {
    
            JavaDirectoryService directoryService = JavaDirectoryService.getInstance();
    
            //当前工程
            Project project = actionEvent.getProject();
            map.put("NAME", text);
            map.put("PACKAGE_NAME", Util.getPackageName(project));
    
            //鼠标右键所选择的路径
            IdeView ideView = actionEvent.getRequiredData(LangDataKeys.IDE_VIEW);
            PsiDirectory directory = ideView.getOrChooseDirectory();
    
            assert directory != null;
            if (directory.getName().contains("contract") && directory.findFile(text + "Contract.java") == null) {
                directoryService.createClass(directory, text + "Contract", "GenerateContractFile", true, map);
            }
    
            if (directory.getName().contains("model") && directory.findFile(text + "Model.java") == null) {
                directoryService.createClass(directory, text + "Model", "GenerateModelFile", true, map);
            }
    
            if (directory.getName().contains("presenter") && directory.findFile(text + "Presenter.java") == null) {
                directoryService.createClass(directory, text + "Presenter", "GeneratePresenterFile", true, map);
            }
    
    
        }
    
        @Override
        public void onCancel() {
            mvpHandler.setVisible(false);
        }
    }
    

    dialog: (action下,右键,new dialog)

    public class MvpHandler extends JDialog {
        private JPanel contentPane;
        private JButton buttonOK;
        private JButton buttonCancel;
        private JTextArea textArea1;
        private OnGenerateListener listener;
    
        public void setOnGenerateListener(OnGenerateListener listener) {
            this.listener = listener;
        }
    
        public MvpHandler() {
            setContentPane(contentPane);
            setModal(true);
            getRootPane().setDefaultButton(buttonOK);
    
            buttonOK.addActionListener(e -> listener.onGenerate(textArea1.getText().trim()));
    
            buttonCancel.addActionListener(e -> listener.onCancel());
    
            // call onCancel() when cross is clicked
            setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    listener.onCancel();
                }
            });
    
            // call onCancel() on ESCAPE
            contentPane.registerKeyboardAction(e -> listener.onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        }
    
        public interface OnGenerateListener{
            void onGenerate(String text);
            void onCancel();
        }
    
    }
    

    MvpHandler.form 拖动控件


    dialog.PNG

    Util:

    public class Util {
    
        /**
         * AndroidManifest.xml 获取 app 包名
         * @return
         */
        public static String getPackageName(Project project) {
            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;
        }
    }
    
    • 2: 定制模板:按需求定制
      new - file - xxx.java.ft
      GenerateContractFile.java.ft
    #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")
    package ${PACKAGE_NAME};
    #end
    
    import base.IBaseModel;
    import base.IBaseView;
    import base.IPresenter;
    
    
    public interface ${NAME}Contract{
    
        interface I${NAME}View extends IBaseView{
    
        }
    
        interface I${NAME}Model extends IBaseModel{
    
        }
    
        interface I${NAME}Presenter extends IPresenter<I${NAME}View>{
    
        }
    
    }
    

    GenerateModelFile.java.ft

    #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
    
    import ${PACKAGE_NAME}.contract.${NAME}Contract;
    
    import base.IBaseModel;
    
    
    public class ${NAME}Model implements ${NAME}Contract.I${NAME}Model{
    
        public static IBaseModel newInstance(){
            return new ${NAME}Model();
        }
    
    
    }
    

    GeneratePresenterFile.java.ft

    #if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")
    package ${PACKAGE_NAME}
    ;#end
    
    import ${PACKAGE_NAME}.contract.${NAME}Contract;
    import ${PACKAGE_NAME}.model.${NAME}Model;
    
    import base.BasePresenter;
    
    
    public class ${NAME}Presenter extends BasePresenter<${NAME}Contract.I${NAME}View,${NAME}Contract.I${NAME}Model> implements ${NAME}Contract.I${NAME}Presenter{
    
        @Override
        public ${NAME}Contract.I${NAME}Model bindModel(){
            return (${NAME}Contract.I${NAME}Model)${NAME}Model.newInstance();
        }
    
    
    }
    

    MVP:
    BasePresenter:

    public abstract class BasePresenter<T extends IBaseView,M extends IBaseModel> implements IPresenter<T> {
    
    
        protected T mView;
        protected M mModel;
    
        protected CompositeDisposable mDisposable;
    
        public BasePresenter() {
            this.mModel = bindModel();
            mDisposable = new CompositeDisposable();
        }
    
        public boolean isViewAttached() {
            return mView != null;
        }
    
    
        public void checkViewAttached() {
            if (!isViewAttached()) throw new RuntimeException("未注册View");
        }
    
        @Override
        public void onAttach(T t) {
            mView = t;
            if (mModel == null) {
                throw new NullPointerException("model没有绑定 不能使用");
            }
        }
    
        /**
         * return Model
         */
        public abstract M bindModel();
    
        @Override
        public void onDetach() {
            if (!mDisposable.isDisposed())
                mDisposable.dispose();
            mView = null;
        }
    
    
    }
    
    

    IBaseModel:

    package base;
    
    /**
     * Created by haoran on 2018/4/4.
     */
    
    public interface IBaseModel {
    }
    

    IBaseView :

    package base;
    
    /**
     * Created by haoran on 2017/10/30.
     */
    
    public interface IBaseView {
    
    
        void showLoading();
    
        void hideLoading();
    
        void onError(String message);
    
    }
    

    IPresenter:

    package base;
    
    /**
     * Created by haoran on 2017/10/30.
     */
    
    public interface IPresenter<T> {
    
        void onAttach(T t);
    
        void onDetach();
        
    }
    

    实现类:(引导页为例)... import 忽略
    GuideContract:

    public interface GuideContract {
        interface IGuideView extends IBaseView{
    
            void showGuideImages(List<GuideImgBean.PictureListBean> pictureListBeans);
    
            void showGuideDefaultImage();
        }
    
        interface IGuideModel extends IBaseModel {
            Observable<List<GuideImgBean.PictureListBean>> getGuideImages();
        }
    
        interface IGuidePresenter extends IPresenter<IGuideView> {
    
            void getGuideImages();
    
        }
    
    }
    

    GuideModel:

    public class GuideModel implements GuideContract.IGuideModel {
    
    
        public static IBaseModel newInstance() {
            return new GuideModel();
        }
    
        public Observable<List<GuideImgBean.PictureListBean>> getGuideImages() {
            return ApiHelper.get().getGuideImages(new JSONObject());
        }
    }
    

    GuidePresenter:

    public class GuidePresenter extends BasePresenter<GuideContract.IGuideView, GuideContract.IGuideModel> implements GuideContract.IGuidePresenter {
    
    
        @Override
        public void getGuideImages() {
            checkViewAttached();
            mDisposable.add(mModel.getGuideImages().subscribe(pictureList -> {
                if (pictureList == null || pictureList.size() == 0) {
                    mView.showGuideDefaultImage();
                } else {
                    mView.showGuideImages(pictureList);
                }
            }, throwable -> mView.showGuideDefaultImage()));
        }
    
        @Override
        public GuideContract.IGuideModel bindModel() {
            return (GuideContract.IGuideModel) GuideModel.newInstance();
        }
    }
    

    根据自己实际的mvp实现类定制模板

    • 3: 打包插件,as引入插件,使用插件(插件开发调试忽略)
      打包:idea里 build - Prapare Pulgine Module 'xxx' for Developer
      项目目录下的jar包
      打开as -- setting pulgine import from disk : jar 包目录
      重启as
      选中目录(含contract , model ,presenter) alt + i(快捷键可在plugine.xml配置)
      或 右键 - new - 点击插件图标
      输入 名称(XXX)--- 生成 java 类
      generate1.PNG
      点击ok
      generate2.PNG

    感谢:)

    相关文章

      网友评论

        本文标题:为AndroidStudio开发mvp插件(MvpHelper)

        本文链接:https://www.haomeiwen.com/subject/prmxkftx.html