这篇接着 IDEA 用户界面组件(一) 继续介绍下其他的一些组件。
Popups
Popups 是 IntelliJ 平台中广泛使用弹出窗口 - 没有显式的关闭按钮并在失去焦点时自动消失。个人感觉跟 Action 类似。
IntelliJ 平台提供了 JBPopupFactory 接口,来创建显示不同类型组件的弹出窗口。
createComponentPopupBuilder()
通用的创建方式,允许显示任何 Swing 组件,也就是可以在弹出的窗口中嵌套前面介绍过的 JComponent,而 JComponent 中就可以自定义任何我们想要显示的内容。
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("这是一个ComponentPopup"), BorderLayout.CENTER);
panel.add(new JButton("上一个"), BorderLayout.NORTH);
panel.add(new JButton("下一个"), BorderLayout.SOUTH);
JBPopup jbPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, null).createPopup();
jbPopup.showCenteredInCurrentWindow(project);
}
}
createPopupChooserBuilder()
可以传入一个普通的 java.util.List,根据传入的内容自动生成一个列表项。
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);
}
}
createConfirmation()
创建一个两个选项的选择弹窗,并可以根据选择的内容执行不同的操作。
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("这是个确认创建", ()-> System.out.println("选择了YES"), 1);
popup.showCenteredInCurrentWindow(project);
}
}
createActionGroupPopup()
用于展示 Action Group 中的 actions,并执行用户选择的 action。官方文档中给的示例是: Edit / Find Usages / Recent Find Usages 中显示最近的的查询记录,应该就是下面这个。
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;
}
}
}
Action Group 中除了可以使用箭头键进行选择外,还可以通过传递 JBPopupFactory.ActionSelectionAid 枚举中的常量之一,来选择通过输入序号或者输入部分文本来快速选择 action。
其他说明
创建 Popup 后,您需要通过调用 show() 方法来显示它。您可以通过调用 showInBestPositionFor() 让 IntelliJ 平台根据上下文自动选择位置,或者通过 showUnderneathOf() 和 showInCenterOf() 等方法显式指定位置。
如果您需要在弹出窗口关闭时执行某些操作,您可以使用 addListener() 方法为其附加监听器。
如果只是想简单的创建一个通知内容,可以通过 JBPopupFactory.getInstance().createMessage("这是一个消息").showInFocusCenter(); 来创建一个很简单的消息。
Notifications
要显示一些通知信息,可以使用 Dialogs、Popup,但通常来说显示非模态通知的方法是使用 Notifications 类。
它有两个主要优点:
- 用户可以控制每种通知类型的显示方式,通过 Settings/Preferences | Appearance & Behavior | Notifications
- 所有显示的通知都收集在事件日志工具窗口中,供以后查看
通知的文本支持 HTML 标记。
使用 Notification.addAction(AnAction) 可以在内容下方添加链接,通过 NotificationAction 可以更方便的使用。
可以通过 Notification 构造函数的 groupId 参数指定通知类型。用户可以在 Settings/Preferences | Appearance & Behavior | Notifications 中选择每种通知类型对应的显示类型。
普通 Notification
要通过首选项指定显示类型,需要使用 NotificationGroup 创建通知,下面是使用 NotificationGroup 方式来创建 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("这是一个 Notification INFORMATION", NotificationType.INFORMATION)
.notify(project);
NotificationGroupManager.getInstance().getNotificationGroup("Custom Notification Group WARNING")
.createNotification("这是一个 Notification WARNING", NotificationType.WARNING)
.notify(project);
NotificationGroupManager.getInstance().getNotificationGroup("Custom Notification Group ERROR")
.createNotification("这是一个 Notification ERROR", NotificationType.ERROR)
.notify(project);
}
}
执行后在 IDEA 的右下角就可以看到通知出现,应该是同时最多能展示 2 个 Notification,创建的 INFORMATION 并没有同时展示出来。同时在 Event Log 里面可以看到通知记录。
带 HTML 标记的 Notification
下面创建了一个带有 HTML 标签的通知消息,不过貌似对 HTML 标签的支持不是特别好。
public class BalloonHtmlText extends AnAction {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (Objects.isNull(project)) {
return;
}
// 创建一个消息
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());
}
}
File and Class Choosers
通过 Dialog
要让用户选择一个文件、目录或多个文件,可以使用 FileChooser.chooseFiles() 方法。这个有多个重载方法。最好用的方式选择返回 void 的方法,并传入一个接收所选文件列表作为参数的回调。类似下面的方法:
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 类控制可以选择哪些文件。构造函数参数指定是否可以选择文件和(或)目录,以及是否允许多选(详细说明请参见 FileChooserDescriptorFactory)。
要对允许的选择进行更细粒度的控制,可以覆写 isFileSelectable() 方法。还可以通过覆写 getIcon()、getName() 和 getComment() 方法来自定义文件的呈现方式。需要注意的是,macOS 系统对大多数的自定义都不支持。如果确实想要修改,则需要使用重载的 chooseFiles() 来显示标准的 IntelliJ 平台对话框。
通过 Textfield
使用文件选择器的一种非常常见的方法是使用文本字段输入路径,并使用省略号按钮 (...) 来显示文件选择器。要创建这样的控件,请使用 TextFieldWithBrowseButton 组件,并对其调用 addBrowseFolderListener() 方法来设置文件选择器。
通过 Tree
通过 TreeFileChooserFactory 类可以使用另一种选择文件的 UI。当使用输入文件名来搜索选择文件时,这种 UI 的效果是最好的。
这个 API 显示的对话框有两个选项卡:
- 一个是显示项目结构
- 另一个是显示类似于 Navigate | File 的文件列表。
要显示对话框,请在 createFileChooser() 返回的选择器上调用 showDialog() 方法。通过调用 getSelectedFile() 来获得用户的选择。
Class 文件选择
如果想提供选择 Java 类的功能,可以使用 TreeClassChooserFactory 类。其不同的方法允许指定获取类的范围,可以将选择限制为特定类的子类或接口的实现,以及包含或排除内部类等。
UI 的效果与 TreeFileChooserFactory 非常类似。
Package 选择
如果要选择 Java 包,可以使用 PackageChooserDialog 类。这个类继承自 DialogWrapper,使用起来与前面介绍的 DialogWrapper 一致。
下面是一个简单的示例,集合了上面介绍的 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;
}
}
}
这是整个 Dialog 的显示样式。
下面是分别使用 5 种文件选择器的文件选择 UI 效果。
普通文件选择
Textfield 文件选择
Tree 文件选择
Class 文件选择
Package 选择