查看原文
其他

教你开发一款属于自己的Android Studio插件

寒云清语 郭霖 2019-05-14



今日科技快讯


继摩拜单车、小蓝单车之后,哈啰近日也将涨价提上日程。21世纪经济报道记者发现,哈啰出行客户端显示:哈啰单车从4月15日起在北京地区实行新的计费规则,每15分钟1元。此外,在部分区域,哈啰单车的包月价格也有所上涨,去年为11.9元一个月,现在则是14.9元。


作者简介


本篇文章来自 寒云清语 的投稿,和大家分享了如何开发一款Android Studio插件,希望对大家有所帮助!

寒云清语 博客地址:

https://juejin.im/user/585571ef570c350069f7b067


前言


在我们使用Android Studio开发的过程中,插件(Plugin)绝对是一个非常给力的助力。我们常用的的插件,比如说:GsonFormat、Android ButterKnife Zelezny、findBugs-IDEA、JsonOnlineViewer等等等,当然这不是我们今天要说的重点,今天的主题是,我们能不能自己开发一款能实现自己的需求的插件呢?答案当然是,可以的!


工具:IDEA


IDEA的下载和安装我们就不说了。直接去官网下载就可以了。随便提一下,官网提供了旗舰版(Ultimate,收费)和社区版(Community,免费),这里开发插件使用社区版就可以了。


开始Coding啦


对于IDEA插件的开发,官方文档进行了比较详细的描述,当然可能要考验考验你的英语阅读能力了,因为是纯英文的。

新建项目

项目结构

我们还要基本了解了解项目的结构,我们就介绍介绍用到的几个文件吧。

  • resources文件夹

顾名思义,这是资源文件夹,我们的图片资源需要本文件夹下。如图所示,我是新建了一个icons文件夹,并放入了一张gradle.svg的图片。plugin.xml文件有点像是配置文件,插件的名称,版本,描述,更新日志,以及Action的声明均是在这里进行,这个我们在后文中会详细说明。

  • src文件夹

我们的代码文件均是在这里进行,如图所示,我已找自己的习惯,进行了文件夹的新建和命名。

  • lib文件夹

(项目新建后没有的话,自己新建一个文件夹,命名为lib即可)

新建Action

这里的Action就好像Android开发中的的Activity。

  • 我们右键action文件夹

新建 -> Plugin DevKit -> Action

  • Action信息填写

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.ui.Messages;

public class MyPluginAction extends AnAction {

    @Override
    public void actionPerformed(AnActionEvent e) {
        Messages.showMessageDialog("我是一条消息""提示", Messages.getInformationIcon());
    }
}
  • 测试Action

我们编写成功后点击Run按钮或是Shift + F10即可。

我们执行代码后,会自动打开一个新的IDEA,这个IDEA是已经安装了我们插件的。如图可以看到,在Build下Build Module后面已经多了一个MyPlugin的项目。我们点击,立即出现一个对话框。当然,我们直接点击我们设置的快捷键(Ctrl + Alt+ 8)也可以。

一个简单的Demo

我这里简单的实现一个Demo。需求如下:

  • 位置位于Tool组最后(快捷键:Ctrl + Alt + 5),而且需要一个图标。

  • 点击后弹出对话框,显示三方库和版本

  • 选择项目后,即可自动导入三方库导入代码

  • 可以从网络获取最新版本,可以重置到初始值,可以支持Kotlin语言

  • Action的新建我就不再说了

由于这里需要在两个位置显示,而且需要图标,所以我们需要在plugin.xml中添加点代码。

<actions>
        <action id="JyyPlugin.AutoGradleAction" class="com.jiangyy.action.AutoGradleAction" text="AutoGradle"
                icon="/META-INF/pluginIcon.svg"
                description="auto gradle">

            <add-to-group group-id="ToolbarRunGroup" anchor="last"/>
            <add-to-group group-id="ToolsMenu" anchor="last"/>
            <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt NUMPAD5"/>
        </action>
    </actions>
  • 新建对话框

我们需要显示一个相对复杂的对话框,所以我们要自定义布局。我们右键ui文件夹,New一个Dialog,随便去一个名字即可

  • 自定义布局

新建Dialog后,会生成两个文件HomeDialog.java和HomeDialog.form,这有点像是Android中的java和xml文件。form文件是可视化操作,很是方便。这是我自定义的布局。

  • 添加表格

我们在form文件拖动控件后,要给每个控件都命名一个field name后才能使用。我在上图中家加了四个JButton和一个JCheckBox。上部分的空白是一个JBanel,命名为了tablePane。会用来放一个表格,用于显示列表数据。

// 声明一个JBTable
JBTable table = new JBTable(new MyTableModel());
table.setPreferredScrollableViewportSize(new Dimension(800300));
table.setFillsViewportHeight(true);
// 声明一个JBScrollPane,把JBTable放进去
JBScrollPane scrollPane = new JBScrollPane(table);
tablePane.setLayout(new GridLayout(10));
// 在tablePane中加入scrollPane
tablePane.add(scrollPane);
  • 显示本地数据

我们要设置表格的列标题,并填充数据,就需要自定义TableModel

private class MyTableModel extends AbstractTableModel {

        private String[] columnNames = {"""Repository""Version""Address"};

        @Override
        public int getRowCount() {
            return ALL_DATA.length;
        }

        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public String getColumnName(int column) {
            return columnNames[column];
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return getValueAt(0, columnIndex).getClass();
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == 0 || columnIndex == 2;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Object o;
            switch (columnIndex) {
                case 0: {
                    o = ALL_DATA[rowIndex].isChoose();
                    break;
                }
                case 1: {
                    o = ALL_DATA[rowIndex].getName();
                    break;
                }
                case 2: {
                    o = ALL_DATA[rowIndex].getVersion();
                    break;
                }
                case 3: {
                    o = "Github";
                    break;
                }
                default: {
                    o = "";
                    break;
                }
            }
            return o;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            switch (columnIndex) {
                case 0:
                    ALL_DATA[rowIndex].setChoose((boolean) aValue);
                    break;
                case 2:
                    ALL_DATA[rowIndex].setVersion((String) aValue);
                    break;
                default:
                    break;
            }
            fireTableCellUpdated(rowIndex, columnIndex);
        }
    }
  • 选中后添加代码

我们在当前界面获取光标的位置,在后面添加代码即可。

private void insetStringAfterOffset(AnActionEvent e, Repository[] data) {
        Editor editor = e.getRequiredData(CommonDataKeys.EDITOR);
        CaretModel caretModel = editor.getCaretModel();
        int offset = caretModel.getOffset(); // 当前光标位置
        WriteCommandAction.runWriteCommandAction(e.getProject(), new Runnable() {
            @Override
            public void run() {
                for (Repository resp : data) {
                    if (resp.isChoose()) {
                        String impl = "\n    // " + resp.getName() + "\n    implementation \'" + resp.getGradle() + resp.getVersion() + "\'";
                        if (!resp.getProcessor().isEmpty()) {
                            if (kotlinCheckBox.isSelected()) {
                                impl += "\n    kapt \'" + resp.getProcessor() + resp.getVersion() + "\'";
                            } else {
                                impl += "\n    annotationProcessor \'" + resp.getProcessor() + resp.getVersion() + "\'";
                            }
                        }
                        editor.getDocument().insertString(offset, impl);
                    }
                }
            }
        });
          // 显示通知消息
        showNotification("implementation""Auto Gradle""implementation success");

    }
  • 显示通知消息

这里的通知消息是会在右下角显示的消息,有点像Android中的Toast。

        private void showNotification(String displayId, String title, String message) {
        NotificationGroup noti = new NotificationGroup(displayId, NotificationDisplayType.BALLOON, true);
        noti.createNotification(
                title,
                message,
                NotificationType.INFORMATION,
                null
        ).notify(event.getProject());
    }
  • 获取项目的最新版本

获取最新版本,我用了github的API,获取项目最新Release的接口。Json解析用了fastjson,网络请求用了OkHttp和Okio。下载JAR包,复制进lib,add as library即可,和Android是一样的,

private void getLastestTag(int row, JBTable table) {
        Repository repository = ALL_DATA[row];
        String url = "https://api.github.com/repos/" + repository.getUser() + "/" + repository.getName() + "/releases/latest";
        OkHttpClient client = new OkHttpClient();
        final Request request = new Request.Builder()
                .url(url)
                .get()//默认就是GET请求,可以不写
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                showNotification(repository.getName(), "错误提示", repository.getName() + " 版本查询失败");
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Resp bean = JSON.parseObject(response.body().string(), Resp.class);
                table.setValueAt(bean.getTag_name(), row, 2);
                showNotification(repository.getName(), "更新成功", repository.getName() + " 更新完成:" + bean.getTag_name());
            }
        });

    }
  • 编写插件信息,测试和总结

以上就是主要的代码逻辑,开发完成后,在plugin.xml文件中编写插件的基本信息。包括:插件ID,名称,版本号,开发者信息,插件描述和更新信息。

<idea-plugin>
    <id>com.jiangyy.auto-gradle</id>
    <name>AutoGradle</name>
    <version>1.0.0</version>
    <vendor email="jiangyychn@gmail.com" url="http://www.github.com/jyygithub/AutoGradle">
        http://www.github.com/jyygithub/AutoGradle
    </vendor>

    <description><![CDATA[
      <h1>AutoGradle</h1>
      <br/>
      <p>
      <b>
      <a href="https://github.com/jyygithub/AutoGradle">GitHub</a> |
      <a href="https://github.com/jyygithub/AutoGradle/issues">Issues</a> |
      </b>
      </p>
      <br/>
      <h2>A auto import gradle project plugin.</h2>
      <br/>
      <h2>Usage:</h2>
      <br/>
      <ul>
        <li>Ctrl + Alt + 5</li>
        <li>Tool -> AutoGradle</li>
        <li>Click the button below</li>
      </ul>
      <br/>
      <p><img src="http://jiangyunyang.com//plugin/AutoGradle/img/toolbar_logo.png" width="750" height="30" alt="toolbar_logo.png"/></p>
      <br/>
      <h2>The following figure is the operation interface</h2>
      <br/>
      <p><img src="http://jiangyunyang.com//plugin/AutoGradle/img/home_dialog.png" width="750" height="370" alt="screenshots.png"/></p>
      <br/>
      <p><a href="https://github.com/jyygithub/AutoGradle/issues">Send feedback</a></p>
    ]]></description>

    <change-notes><![CDATA[
      <ul>
        <li>初始版本</li>
      </ul>
      <a href="https://github.com/jyygithub/AutoGradle/blob/master/CHANGELOG.md"><b>Full Changelog History</b></a>
    ]]></change-notes>

    <!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
    <idea-version since-build="173.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>
    -->


    <!--uncomment to enable plugin in all products-->
    <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
    </extensions>

    <actions>
        <action id="JyyPlugin.AutoGradleAction" class="com.jiangyy.action.AutoGradleAction" text="AutoGradle"
                icon="/META-INF/pluginIcon.svg"
                description="auto gradle">

            <add-to-group group-id="ToolbarRunGroup" anchor="last"/>
            <add-to-group group-id="ToolsMenu" anchor="last"/>
            <keyboard-shortcut keymap="$default" first-keystroke="ctrl alt NUMPAD5"/>
        </action>
    </actions>

</idea-plugin>

完整项目代码已上传到Github。这是最后的简介效果图:


打包上架


生成压缩包

插件编写完成后,点击 Build -> Prepare Plguin Module 'AutoGradle' For Deployment 即可在项目根目录生成一个同名zip文件。

上传到插件中心

  • 进入插件中心,登录你的账号。

  • 选择Upload plugin

  • 上传插件,完善插件信息,提交即可。(上传成功后,需要等待审核通过才能在插件市场搜到)


总结


Github地址:

https://github.com/jyygithub/AutoGradle

官方文档:

http://www.jetbrains.org/intellij/sdk/docs/welcome.html

IDEA官网:

https://www.jetbrains.com/idea/download/#section=windows


推荐阅读:

基础为王的今天,不可忽视的Java多线程知识

Flutter的踩坑感悟分享,分析Android、iOS的实现异同

免Root实现Android静默安装(非无障碍)


欢迎关注我的公众号,学习技术或投稿

长按上图,识别图中二维码即可关注

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存