IDEA 用戶界面組件(二)


這篇接著 IDEA 用戶界面組件(一) 繼續(xù)介紹下其他的一些組件。

Popups

Popups 是 IntelliJ 平臺(tái)中廣泛使用彈出窗口 - 沒有顯式的關(guān)閉按鈕并在失去焦點(diǎn)時(shí)自動(dòng)消失。個(gè)人感覺跟 Action 類似。

IntelliJ 平臺(tái)提供了 JBPopupFactory 接口,來創(chuàng)建顯示不同類型組件的彈出窗口。

createComponentPopupBuilder()

通用的創(chuàng)建方式,允許顯示任何 Swing 組件,也就是可以在彈出的窗口中嵌套前面介紹過的 JComponent,而 JComponent 中就可以自定義任何我們想要顯示的內(nèi)容。

public class PopupComponentAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (Objects.isNull(project)) {
            return;
        }

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(new JLabel("這是一個(gè)ComponentPopup"), BorderLayout.CENTER);
        panel.add(new JButton("上一個(gè)"), BorderLayout.NORTH);
        panel.add(new JButton("下一個(gè)"), BorderLayout.SOUTH);

        JBPopup jbPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, null).createPopup();
        jbPopup.showCenteredInCurrentWindow(project);
    }
}

ComponentPopup 示例

createPopupChooserBuilder()

可以傳入一個(gè)普通的 java.util.List,根據(jù)傳入的內(nèi)容自動(dòng)生成一個(gè)列表項(xiàng)。

public class PopupChooserAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (Objects.isNull(project)) {
            return;
        }

        JBPopup chooser = JBPopupFactory.getInstance()
                .createPopupChooserBuilder(Lists.newArrayList("abc", "def")).createPopup();
        chooser.showCenteredInCurrentWindow(project);
    }
}
PopupChooser 示例

createConfirmation()

創(chuàng)建一個(gè)兩個(gè)選項(xiàng)的選擇彈窗,并可以根據(jù)選擇的內(nèi)容執(zhí)行不同的操作。

public class PopupConfirmationAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (Objects.isNull(project)) {
            return;
        }

        ListPopup popup = JBPopupFactory.getInstance()
                .createConfirmation("這是個(gè)確認(rèn)創(chuàng)建", ()-> System.out.println("選擇了YES"), 1);
        popup.showCenteredInCurrentWindow(project);
    }
}
Confirmation 示例

createActionGroupPopup()

用于展示 Action Group 中的 actions,并執(zhí)行用戶選擇的 action。官方文檔中給的示例是: Edit / Find Usages / Recent Find Usages 中顯示最近的的查詢記錄,應(yīng)該就是下面這個(gè)。

搜索歷史
public class PopupActionGroupAction extends AnAction {

    public static final int NUM_10 = 10;

    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        new PopupGroupAction().actionPerformed(e);
    }

    public static class PopupGroupAction extends DefaultActionGroup {
        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            Project project = e.getProject();
            if (Objects.isNull(project)) {
                return;
            }
            ListPopup popup = JBPopupFactory.getInstance()
                    .createActionGroupPopup("CreateActionGroupPopup", this, e.getDataContext(),
                            JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
            popup.showCenteredInCurrentWindow(project);
        }

        @Override
        public AnAction @NotNull [] getChildren(@Nullable AnActionEvent e) {
            AnAction[] actions = new AnAction[NUM_10];
            for (int i = 0; i < NUM_10; i++) {
                actions[i] = new AnAction("index:" + i) {
                    @Override
                    public void actionPerformed(@NotNull AnActionEvent e) {
                        System.out.println(e.getPresentation().getText());
                    }
                };
            }
            return actions;
        }
    }
}
PopupActionGroup 示例

Action Group 中除了可以使用箭頭鍵進(jìn)行選擇外,還可以通過傳遞 JBPopupFactory.ActionSelectionAid 枚舉中的常量之一,來選擇通過輸入序號(hào)或者輸入部分文本來快速選擇 action。

其他說明

創(chuàng)建 Popup 后,您需要通過調(diào)用 show() 方法來顯示它。您可以通過調(diào)用 showInBestPositionFor() 讓 IntelliJ 平臺(tái)根據(jù)上下文自動(dòng)選擇位置,或者通過 showUnderneathOf() 和 showInCenterOf() 等方法顯式指定位置。

如果您需要在彈出窗口關(guān)閉時(shí)執(zhí)行某些操作,您可以使用 addListener() 方法為其附加監(jiān)聽器。

如果只是想簡(jiǎn)單的創(chuàng)建一個(gè)通知內(nèi)容,可以通過 JBPopupFactory.getInstance().createMessage("這是一個(gè)消息").showInFocusCenter(); 來創(chuàng)建一個(gè)很簡(jiǎn)單的消息。

Notifications

要顯示一些通知信息,可以使用 Dialogs、Popup,但通常來說顯示非模態(tài)通知的方法是使用 Notifications 類。

它有兩個(gè)主要優(yōu)點(diǎn):

  • 用戶可以控制每種通知類型的顯示方式,通過 Settings/Preferences | Appearance & Behavior | Notifications
  • 所有顯示的通知都收集在事件日志工具窗口中,供以后查看

通知的文本支持 HTML 標(biāo)記。

使用 Notification.addAction(AnAction) 可以在內(nèi)容下方添加鏈接,通過 NotificationAction 可以更方便的使用。

可以通過 Notification 構(gòu)造函數(shù)的 groupId 參數(shù)指定通知類型。用戶可以在 Settings/Preferences | Appearance & Behavior | Notifications 中選擇每種通知類型對(duì)應(yīng)的顯示類型。

普通 Notification

要通過首選項(xiàng)指定顯示類型,需要使用 NotificationGroup 創(chuàng)建通知,下面是使用 NotificationGroup 方式來創(chuàng)建 Notification。

 <extensions defaultExtensionNs="com.intellij">
    <notificationGroup id="Custom Notification Group INFORMATION" displayType="BALLOON" />
    <notificationGroup id="Custom Notification Group WARNING" displayType="BALLOON" />
    <notificationGroup id="Custom Notification Group ERROR" displayType="BALLOON" />
  </extensions>
public class Notification extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (Objects.isNull(project)) {
            return;
        }

        NotificationGroupManager.getInstance().getNotificationGroup("Custom Notification Group INFORMATION")
                .createNotification("這是一個(gè) Notification INFORMATION", NotificationType.INFORMATION)
                .notify(project);

        NotificationGroupManager.getInstance().getNotificationGroup("Custom Notification Group WARNING")
                .createNotification("這是一個(gè) Notification WARNING", NotificationType.WARNING)
                .notify(project);

        NotificationGroupManager.getInstance().getNotificationGroup("Custom Notification Group ERROR")
                .createNotification("這是一個(gè) Notification ERROR", NotificationType.ERROR)
                .notify(project);
    }
}

執(zhí)行后在 IDEA 的右下角就可以看到通知出現(xiàn),應(yīng)該是同時(shí)最多能展示 2 個(gè) Notification,創(chuàng)建的 INFORMATION 并沒有同時(shí)展示出來。同時(shí)在 Event Log 里面可以看到通知記錄。

Notification
Event Log

帶 HTML 標(biāo)記的 Notification

下面創(chuàng)建了一個(gè)帶有 HTML 標(biāo)簽的通知消息,不過貌似對(duì) HTML 標(biāo)簽的支持不是特別好。

public class BalloonHtmlText extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
        Project project = e.getProject();
        if (Objects.isNull(project)) {
            return;
        }

        // 創(chuàng)建一個(gè)消息
        final JFrame jFrame = WindowManager.getInstance().getFrame(project);
        Balloon balloon = JBPopupFactory.getInstance().createHtmlTextBalloonBuilder("<form>姓名:<input type=\"text\" name=\"name\"/>住址:<input type=\"text\" name=\"address\"/><button type=\"submit\">提交</button></form>", MessageType.INFO, e1 -> {
        }).createBalloon();
        balloon.showInCenterOf(Objects.requireNonNull(jFrame).getRootPane());
    }
}
HTML Balloon

File and Class Choosers

通過 Dialog

要讓用戶選擇一個(gè)文件、目錄或多個(gè)文件,可以使用 FileChooser.chooseFiles() 方法。這個(gè)有多個(gè)重載方法。最好用的方式選擇返回 void 的方法,并傳入一個(gè)接收所選文件列表作為參數(shù)的回調(diào)。類似下面的方法:

  public static void chooseFiles(@NotNull final FileChooserDescriptor descriptor,
                                 @Nullable final Project project,
                                 @Nullable final VirtualFile toSelect,
                                 @NotNull final Consumer<? super List<VirtualFile>> callback) {
    chooseFiles(descriptor, project, null, toSelect, callback);
  }

FileChooserDescriptor 類控制可以選擇哪些文件。構(gòu)造函數(shù)參數(shù)指定是否可以選擇文件和(或)目錄,以及是否允許多選(詳細(xì)說明請(qǐng)參見 FileChooserDescriptorFactory)。

要對(duì)允許的選擇進(jìn)行更細(xì)粒度的控制,可以覆寫 isFileSelectable() 方法。還可以通過覆寫 getIcon()、getName() 和 getComment() 方法來自定義文件的呈現(xiàn)方式。需要注意的是,macOS 系統(tǒng)對(duì)大多數(shù)的自定義都不支持。如果確實(shí)想要修改,則需要使用重載的 chooseFiles() 來顯示標(biāo)準(zhǔn)的 IntelliJ 平臺(tái)對(duì)話框。

通過 Textfield

使用文件選擇器的一種非常常見的方法是使用文本字段輸入路徑,并使用省略號(hào)按鈕 (...) 來顯示文件選擇器。要?jiǎng)?chuàng)建這樣的控件,請(qǐng)使用 TextFieldWithBrowseButton 組件,并對(duì)其調(diào)用 addBrowseFolderListener() 方法來設(shè)置文件選擇器。

通過 Tree

通過 TreeFileChooserFactory 類可以使用另一種選擇文件的 UI。當(dāng)使用輸入文件名來搜索選擇文件時(shí),這種 UI 的效果是最好的。

這個(gè) API 顯示的對(duì)話框有兩個(gè)選項(xiàng)卡:

  • 一個(gè)是顯示項(xiàng)目結(jié)構(gòu)
  • 另一個(gè)是顯示類似于 Navigate | File 的文件列表。

要顯示對(duì)話框,請(qǐng)?jiān)?createFileChooser() 返回的選擇器上調(diào)用 showDialog() 方法。通過調(diào)用 getSelectedFile() 來獲得用戶的選擇。

Class 文件選擇

如果想提供選擇 Java 類的功能,可以使用 TreeClassChooserFactory 類。其不同的方法允許指定獲取類的范圍,可以將選擇限制為特定類的子類或接口的實(shí)現(xiàn),以及包含或排除內(nèi)部類等。

UI 的效果與 TreeFileChooserFactory 非常類似。

Package 選擇

如果要選擇 Java 包,可以使用 PackageChooserDialog 類。這個(gè)類繼承自 DialogWrapper,使用起來與前面介紹的 DialogWrapper 一致。

下面是一個(gè)簡(jiǎn)單的示例,集合了上面介紹的 5 種文件選擇器。

public class FileChooseAction extends AnAction {
    @Override
    public void actionPerformed(@NotNull AnActionEvent e) {
       new CustomDialog(e).show();
    }

    public static class CustomDialog extends DialogWrapper{

        AnActionEvent anActionEvent;

        public CustomDialog(AnActionEvent anActionEvent) {
            super(true);
            this.anActionEvent = anActionEvent;
            init();
        }

        @Override
        protected @Nullable JComponent createCenterPanel() {

            Project project = anActionEvent.getProject();
            if (Objects.isNull(project)) {
                return null;
            }
            PsiFile psiFile = anActionEvent.getData(CommonDataKeys.PSI_FILE);
            if (psiFile == null) {
                return null;
            }

            JPanel panel = new JPanel(new FlowLayout());
            panel.setVisible(true);

            // 添加普通文件選擇
            JButton fileChooseBtn = new JButton("普通文件選擇");
            fileChooseBtn.addActionListener(event ->
                    FileChooser.chooseFiles(FileChooserDescriptorFactory.createSingleFileDescriptor(),
                            project, null, (s) -> s.forEach(f -> System.out.println(f.getName()))));
            panel.add(fileChooseBtn);

            // 添加帶瀏覽按鈕的文本框控件
            TextFieldWithBrowseButton browseButton = new TextFieldWithBrowseButton();
            browseButton.addBrowseFolderListener(new TextBrowseFolderListener(FileChooserDescriptorFactory.createSingleFileDescriptor()));
            panel.add(browseButton);

            // 添加 Tree 文件選擇
            JButton treeFileChooseBtn = new JButton("Tree 文件選擇");
            treeFileChooseBtn.addActionListener(event -> {
                TreeFileChooser chooser = TreeFileChooserFactory.getInstance(project)
                        .createFileChooser("Tree 文件選擇", psiFile, FileTypes.PLAIN_TEXT, null);
                chooser.showDialog();
                System.out.println(chooser.getSelectedFile());
            });
            panel.add(treeFileChooseBtn);

            // 添加 Class 文件選擇
            JButton treeClassChooseBtn = new JButton("Class 文件選擇");
            treeClassChooseBtn.addActionListener(event -> {
                TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project).createProjectScopeChooser("Class 文件選擇");
                chooser.showDialog();
                System.out.println(chooser.getSelected());
            });
            panel.add(treeClassChooseBtn);

            // 添加 Java 包選擇
            JButton packageChooseBtn = new JButton("Java 包選擇");
            packageChooseBtn.addActionListener(event -> {
                PackageChooserDialog chooser = new PackageChooserDialog("Java 包選擇", project);
                if (chooser.showAndGet()) {
                    PsiPackage aPackage = chooser.getSelectedPackage();
                    System.out.println(aPackage.getName());
                }
            });
            panel.add(packageChooseBtn);

            return panel;
        }
    }
}

這是整個(gè) Dialog 的顯示樣式。

5 種文件選擇器

下面是分別使用 5 種文件選擇器的文件選擇 UI 效果。

普通文件選擇

普通文件選擇

Textfield 文件選擇

Textfield 文件選擇

Tree 文件選擇

Tree 文件選擇
Tree 文件選擇

Class 文件選擇

Class 文件選擇
Class 文件選擇

Package 選擇

Package 選擇
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容