IDEA Plugin,寫一個看股票指數和K線的外掛

小傅哥發表於2021-11-18

作者:小傅哥
部落格:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!?

一、前言

沒招了,不寫點刺激的,你總是不好好看!

以前,我不懂。寫的技術就是技術內容,寫的場景就是場景分析,但從讀者的閱讀我發現,大家更喜歡的是技術與場景結合,尤其是用技術結合那些羞羞答答的場景,雖然嘴上都不說。

本章節主要是想給大家介紹關於在 IDEA 外掛開發中如何使用工具欄和Tab頁,來填充在 IDEA 中底部的窗體,就像 IDEA 中的控制檯一樣。但就這麼寫好像是夠嗆能有人看,因為大家只從技術點來看,很難發現這裡面有什麼應用場景,即使看了好像也不知道這技術能幹啥。

那咋辦,整點刺激的吧。大家不是喜歡賠錢炒股嗎,那就把股票行情K線展示結合到IDEA外掛開發中。可以讓學習外掛的夥伴,都能在寫程式碼疲憊的時候還能看一眼股票,也許在關鍵的時候還能提醒你趕緊丟擲去!

二、需求目的

安全起見,需要在 IDEA 中以一個比較隱祕的角落,開發股票外掛,讓炒股的同學可以在緊張編碼CRUD之餘,不要忘記自己關注的股票購買和拋售。

那麼為了解決這個問題,我們需要幾個技術點,包括:股票資料介面、查詢物件封裝、IDEA 底部窗體和工具欄開發、定時任務掃描、Swing UI、股票程式碼配置和儲存、窗體按鈕事件監聽等。

接下來我們就結合這些技術點,來解決實際的場景問題,看看如何在 IDEA 中開發一個股票外掛。

三、案例開發

1. 工程結構

guide-idea-plugin-tab
├── .gradle
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.guide.idea.plugin 
    │           ├── domain
    │           │    ├── model
    │           │    │    ├── aggregates
    │           │    │    │    └── StockResult.java    
    │           │    │    └── vo  
    │           │    │         ├── Data.java 
    │           │    │         ├── GoPicture.java     
    │           │    │         └── Stock.java       
    │           │    └── service   
    │           │         ├── impl     
    │           │         │    └── StockImpl.java    
    │           │         └── IStock      
    │           ├── factory
    │           │    └── TabFactory.java  
    │           ├── infrastructure
    │           │    ├── DataSetting.java       
    │           │    └── DataState.java     
    │           ├── module
    │           │    ├── RefreshBar.java   
    │           │    ├── SettingBar.java    
    │           │    └── ViewBars.java         
    │           └── ui
    │           │    ├── ConsoleUI.java 
    │           │    ├── ConsoleUI.form
    │           │    ├── GidConfig.java  
    │           │    └── GidConfig.form
    │           └── Config    
    ├── resources
    │   └── META-INF
    │       └── plugin.xml 
    ├── build.gradle  
    └── gradle.properties

原始碼獲取:#公眾號:bugstack蟲洞棧 回覆:idea 即可下載全部 IDEA 外掛開發原始碼

在此 IDEA 外掛工程中,主要分為5塊區域:

  • domain:領域層,提供查詢股票介面的資料服務,如果你是做的一些其他工具型功能,也可以把業務類的內容放到 domain 中實現。
  • factory:工廠層,這裡主要提供的是一個工具窗體生成的入口物件,來建立出我們自己新增的窗體內容。
  • infrastructure:基礎層,提供了資料存放物件,這個資料物件是一個可以落盤的操作,建立好的類配置到 plugin.xml 中即可。這樣我們配置好股票程式碼後,關機重啟 IDEA 也可以把配置讀取出來
  • module:模組,提供用於 UI 窗體使用的一些工具頁操作。比如這裡的 ViewBars 會在 TabFactory 中例項化,用於展示出你新增的窗體。
  • ui:這一部分使用的是 IDEA 中自動拖拽生成的窗體,免去了手寫的複雜性,一些簡單的頁面直接拖拽就可以。這也是一種低程式碼哦!

接下來,我們就分別看下每個核心功能點的實現過程,這個過程中你可以提前把程式碼下載下來,對照著學習會更加容易理解。

  1. 實現 ToolWindowFactory 開發一個底部的窗體,用於承載所需的內容
  2. 左側是側邊工具欄,配置自選股、重新整理股票指數
  3. 右側是2個 tab 頁,分別用於展示股票資料和K線圖,這裡的資料則需要通過股票介面來提供

2. tab 頁窗體

首先這裡我們先使用 IDEA 外掛開發中,Swing UI 功能,拖拽出2個簡單的窗體。有了這樣的一個基本結構大家的腦子裡應該就可以有畫面了。

2.1 自選股配置窗體

public class GidConfig implements Configurable {

    private JPanel mainPanel;
    private JPanel settingPanel;
    private JLabel gidLabel;
    private JTextField gidTextField;

    private ConsoleUI consoleUI;

    public GidConfig(ConsoleUI consoleUI){
        this.consoleUI = consoleUI;
    }

    public JTextField getGidTextField() {
        return gidTextField;
    }

    @Override
    public void apply() throws ConfigurationException {
        List<String> gidList = DataSetting.getInstance().getGids();
        gidList.clear();
        String[] gids = gidTextField.getText().trim().split(",");
        for (String gid : gids) {
            gidList.add(gid.trim());
        }
        // 重新整理資料
        consoleUI.addRows(gidList);
    }

}
  • 在 GidConfig 對應的 java 類中,可以對一些窗體中出現的屬性進行獲取。當使用者點選這個窗體的確認按鈕後,我們可以在 apply 中拿到使用者配置的股票程式碼,並對其進行讀取和設定股票資料。

2.2 股票展示窗體

public class ConsoleUI {

    private JTabbedPane tabbedPane1;
    private JPanel one;
    private JPanel two;
    private JLabel picMin;
    private JTable table;
    private JLabel picDay;

    // 查詢資料服務
    private IStock stock = new StockImpl();

    private DefaultTableModel defaultTableModel = new DefaultTableModel(new Object[][]{}, new String[]{"股票", "程式碼", "最新", "漲跌", "漲幅"});

    public ConsoleUI() {
        // 初始資料
        table.setModel(defaultTableModel);
        addRows(DataSetting.getInstance().getGids());

        // 新增事件
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                int row = table.getSelectedRow();
                Object value = table.getValueAt(row, 1);
                GoPicture goPicture = stock.queryGidGoPicture(value.toString());
                try {
                    // 分鐘K線
                    picMin.setSize(545, 300);
                    picMin.setIcon(new ImageIcon(new URL(goPicture.getMinurl())));

                    // 當日K線
                    picDay.setSize(545, 300);
                    picDay.setIcon(new ImageIcon(new URL(goPicture.getDayurl())));
                } catch (MalformedURLException m) {
                    m.printStackTrace();
                }
            }
        });
    }

    public JTabbedPane getPanel() {
        return tabbedPane1;
    }

    public void addRows(List<String> gids) {
        // 查詢
        List<Data> dataList = stock.queryPresetStockData(gids);

        // 清空
        int rowCount = defaultTableModel.getRowCount();
        for (int i = 0; i < rowCount; i++) {
            defaultTableModel.removeRow(0);
        }

        // 新增
        for (Data data : dataList) {
            defaultTableModel.addRow(new String[]{data.getName(), data.getGid(), data.getNowPri(), data.getIncrease(), data.getIncrePer()});
            table.setModel(defaultTableModel);
        }
    }

}
  • 展示股票的窗體對應的 ConsoleUI 類,主要負責資料的渲染、更新和對每條資料的事件操作,當使用者點選某一條資料以後,就可以到 K線 頁中看到對應的股票指數了。

3. 股票框體設定

在開發完 UI 窗體後,我們還需要使用一個 SimpleToolWindowPanel 的繼承實現類,承載工具欄和頁面的設定。

3.1 設定-工具欄

cn.bugstack.guide.idea.plugin.module.SettingBar

public class SettingBar extends DumbAwareAction {

    private ViewBars panel;

    public SettingBar(ViewBars panel) {
        super("配置股票", "Click to setting", IconLoader.getIcon("/icons/config.svg"));
        this.panel = panel;
    }

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        ShowSettingsUtil.getInstance().editConfigurable(panel.getProject(), new GidConfig(panel.getConsoleUI()));
    }

}
  • 設定工具欄位於自定義外掛皮膚中最左側的位置,用於設定自選股票程式碼。
  • 通過在方法 actionPerformed 中使用 ShowSettingsUtil 工具類啟動 UI 窗體。

3.2 重新整理-工具欄

cn.bugstack.guide.idea.plugin.module.RefreshBar

public class RefreshBar extends DumbAwareAction {

    private ViewBars panel;

    public RefreshBar(ViewBars panel) {
        super("重新整理指數", "Click to refresh", IconLoader.getIcon("/icons/refresh.svg"));
        this.panel = panel;
    }

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        panel.getConsoleUI().addRows(DataSetting.getInstance().getGids());
    }

}
  • 在重新整理工具欄中主要是用於手動觸發重新整理股票最新結果,之所以使用手動重新整理主要是這個介面有查詢次數限制,如果是定時任務一直跑,一會100次的查詢限制就用完了。不過我們這裡也是為了體現專欄內對技術的使用,增加多個設定按鈕,就更容易知道如何新增了

3.3 窗體填充皮膚

cn.bugstack.guide.idea.plugin.module.ViewBars

public class ViewBars extends SimpleToolWindowPanel {

    private Project project;
    private ConsoleUI consoleUI;

    public ViewBars(Project project) {
        super(false, true);
        this.project = project;
        consoleUI = new ConsoleUI();

        // 設定窗體側邊欄按鈕
        DefaultActionGroup group = new DefaultActionGroup();
        group.add(new SettingBar(this));
        group.add(new RefreshBar(this));

        ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("bar", group, false);
        toolbar.setTargetComponent(this);
        setToolbar(toolbar.getComponent());

        // 新增
        JBSplitter splitter = new JBSplitter(false);
        splitter.setSplitterProportionKey("main.splitter.key");
        splitter.setFirstComponent(consoleUI.getPanel());
        splitter.setProportion(0.3f);
        setContent(splitter);
    }

    public Project getProject() {
        return project;
    }

    public ConsoleUI getConsoleUI() {
        return consoleUI;
    }
}
  • 在填皮膚中主要是在我們自定義的外掛中,在左側新增工具欄,其餘位置新增股票展示皮膚。
  • DefaultActionGroup 中可以以此新增設定和重新整理按鈕,並最終填充到 ActionToolbar 裡去,這樣就設定完成了。
  • JBSplitter 是一個分割線,右側填充上我們的股票指數展示皮膚 splitter.setFirstComponent(consoleUI.getPanel())

4. 填充主皮膚到IDEA工具欄

4.1 窗體工廠

cn.bugstack.guide.idea.plugin.factory.TabFactory

public class TabFactory implements ToolWindowFactory {

    @Override
    public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
        // 窗體
        ViewBars viewPanel = new ViewBars(project);
        // 獲取內容工廠的例項
        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        // 獲取 ToolWindow 顯示的內容
        Content content = contentFactory.createContent(viewPanel, "股票", false);
        // 設定 ToolWindow 顯示的內容
        toolWindow.getContentManager().addContent(content, 0);

        // 定時任務,自動重新整理股票
        /* 因每日查詢次數限制,這裡就不開定時任務了,使用者可以自行申請 https://dashboard.juhe.cn/home
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                viewPanel.getConsoleUI().addRows(DataSetting.getInstance().getGids());
            }
        }, 3000, 2000);*/
    }

}
  • 在 TabFactory 中主要包括兩部分,一個是把 ViewBars 填充到整個 toolWindow 中,另外一個是我們目前已經註釋掉的重新整理股票資料的定時任務。
  • 這裡由於股票介面查詢次數限制,所以就把定時任務註釋掉了,否則一會就把可用次數跑沒了。

4.2 配置窗體

plugin.xml

<idea-plugin>
    
    <extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
        <toolWindow id="XUtil"
                    canCloseContents="true"
                    anchor="bottom"
                    factoryClass="cn.bugstack.guide.idea.plugin.factory.TabFactory"
                    icon="/icons/stock.png"
        />

    </extensions>

</idea-plugin>
  • 這裡我們把窗體配置到整個 IDEA 介面的最下方 anchor="bottom" 這個位置既方便又最安全

5. 資料持久化配置

  • 當我們使用 IDEA 進行配置一些基本引數後,例如:Maven、Gradle、Git、簽名資訊等,在日常的關閉和重啟 IDEA 時,這些配置資訊是會儲存下來的,而不會說關閉就沒了。
  • 那麼我們開發的這款外掛需要做的一些自選股票程式碼配置,也要進行儲存,否則不能每次都在 IDEA 啟動時重新設定。所以這裡我們需要用到 plugin.xmlapplicationService 配置上實現了 PersistentStateComponent 的資料設定存放類。

5.1 物件資料

cn.bugstack.guide.idea.plugin.infrastructure.DataState

public class DataState {

    private List<String> gids = new ArrayList<>();

    public List<String> getGids() {
        return gids;
    }

    public void setGids(List<String> gids) {
        this.gids = gids;
    }

}
  • 這個是資料物件類,你可以在這裡設定你需要的屬性存放,就像 gids 一樣,用於存放使用者配置的股票程式碼集合。

5.2 持久資料

cn.bugstack.guide.idea.plugin.infrastructure.DataSetting

@State(name = "DataSetting",storages = @Storage("plugin.xml"))
public class DataSetting implements PersistentStateComponent<DataState> {

    private DataState state = new DataState();

    public static DataSetting getInstance() {
        return ServiceManager.getService(DataSetting.class);
    }

    @Nullable
    @Override
    public DataState getState() {
        return state;
    }

    @Override
    public void loadState(@NotNull DataState state) {
        this.state = state;
    }

     public List<String> getGids(){
        return state.getGids();
     }

}
  • DataSetting 類需要使用到 IDEA 外掛開發的提供的註解 @State 配置持久物件
  • 此外還需要提供一個 getInstance 方法來獲取資料物件例項,那麼在我們實際使用的時候就可以拿到我們配置的物件了並進行設定和讀取資料。

5.3 plugin.xml 配置

<extensions defaultExtensionNs="com.intellij">
    <!-- Add your extensions here -->
    <applicationService serviceImplementation="cn.bugstack.guide.idea.plugin.infrastructure.DataSetting"/>
</extensions>
  • 在建立好資料設定類以後,則需要使用 applicationService 標籤把你的類配置到 plugin.xml 中 extensions 裡面。

6. 股票介面

6.1 技術調研

  • 無論什麼功能開發,在開始之前都需要把這些零碎的事情處理完,才能進行程式碼開發,這個過程也叫做技術調研到設計和評審。就像現在我們需要進行股票資訊的查詢,那麼就需要找到一個可以提供資料查詢的介面,看看這個介面如何申請使用,以及返回的物件都有哪些欄位,是否符合我們的預期。
  • 這裡小傅哥找到了一個聚合資料的介面,不過只能免費100次/天呼叫,如果你有更好的可以更換下。

  • 介面:http://web.juhe.cn:8080/finance/stock/hs?gid=sz000651&key=自己申請 - 這裡的 key 需要自己申請
  • 資料:

6.2 服務封裝

有了股票的查詢介面,接下來就可以對資料做一個查詢和物件轉換了。

cn.bugstack.guide.idea.plugin.domain.service.impl.StockImpl

public class StockImpl implements IStock {

    // 自行申請,股票API,替換key即可【一天可免費呼叫100次】:https://dashboard.juhe.cn/home/
    private final String key = "4bc57728***********f0595";

    @Override
    public List<Data> queryPresetStockData(List<String> gids) {
        List<Data> dataList = new ArrayList<>();
        for (String gid : gids) {
            StockResult stockResult = JSON.parseObject(HttpUtil.get("http://web.juhe.cn:8080/finance/stock/hs?gid=" + gid + "&key=" + key), StockResult.class);
            Stock[] stocks = stockResult.getResult();
            for (Stock stock : stocks) {
                dataList.add(stock.getData());
            }
        }
        return dataList;
    }

}
  • 這裡我們在 domain 領域層中定義資料 vo 物件,以及提供股票查詢服務的封裝。這樣呼叫方就可以直接使用這份資料了,如果你是其他廠商提供的股票查詢介面,也可以進行封裝和更換,做一個介面適配層。

四、測試驗證

  • 如果你下載工程後沒有 Plugin 和一個綠色箭頭,那麼可以按照圖自己配置 :runIde 這樣就可以執行了。

執行效果 - 激動人心的時刻到了,再也不用擔心寫程式碼影響看股票了哦

1. 配置股票

  • 首先你需要在這裡配置你關注的股票程式碼,我配置了3個我看好的。

2. 自選股指數

  • 配置好以後你就可以看到自己的自選股指數了,選中一條以後,在點選K線。
  • 當你需要看最新資料的時候,可以點左側的重新整理按鈕。

3. K線

  • 現在這個就是對應的 K線,是不是還挺香的。目前是加了最近K線和日K線,你還可以擴充套件其他維度的圖。

五、總結

  • 本章節我們使用到了在 IDEA 窗體中新增稍微複雜一些的頁面結構,有側邊欄、有Tab頁,並在需要在這些頁面體中進行互動和通訊。此外還是用到了資料的儲存設定,這個在很多時候開發IDEA外掛裡都會用到。
  • 像是這樣的技術實踐不只是可以用於展示股票資料,你還可以結合自己所需擴充套件屬於你實際場景中需要的內容,比如開發一個資料集中查詢外掛,可以查詢資料庫、ES、Redis等,也可以是所有的工具類集合頁,這些內容會更有技術價值。
  • 當你自己開始主動的向你學習到的一些原始碼、框架、元件、專案等中新增自己想要的功能時,就是你真的開始學習了,否則一個內容看過沒多久也就忘記了。

六、系列推薦

相關文章