第13章:SWT 与操作系统的深度集成
作者:步子哥 (steper@foxmail.com)
13.1 调用系统浏览器
Program.launch()
// 在默认浏览器中打开 URL
try {
Program.launch("https://www.eclipse.org");
} catch (Exception e) {
e.printStackTrace();
}
// 在默认程序中打开文件
try {
Program.launch("C:\\Users\\User\\Documents\\test.txt");
} catch (Exception e) {
e.printStackTrace();
}
费曼解释:Program.launch() 的原理
Program.launch() = 电话转接
- 你(应用)拨打 10086(调用 launch)
- 话务员(SWT)帮你转接到浏览器
- 浏览器(系统默认程序)接听并执行
类比:
Program.launch() = 快递配送
- 你(应用)把包裹(URL/文件)给快递员(launch)
- 快递员把包裹送到收件人(系统默认程序)
- 收件人签收并处理(打开 URL/文件)
检测程序是否存在
// 检测程序是否可用
Program browserProgram = Program.findProgram("html");
if (browserProgram != null) {
System.out.println("浏览器程序:" + browserProgram.getName());
System.out.println("浏览器图标:" + browserProgram.getImageData());
} else {
System.out.println("未找到浏览器程序");
}
// 获取所有可用程序
Program[] programs = Program.getPrograms();
for (Program program : programs) {
System.out.println("程序:" + program.getName() + ",扩展名:" + Arrays.toString(program.getExtensions()));
}
费曼解释:findProgram() 的作用
findProgram() = 查找联系人
- 你想给浏览器打电话(调用 launch)
- 但你不知道浏览器的电话号码
- 你查通讯录(findProgram)
- 找到浏览器的电话号码(返回 Program 对象)
类比:
findProgram() = 查找快递员
- 你想寄快递(调用 launch)
- 但你不知道哪个快递员负责这个区域
- 你查快递员表(findProgram)
- 找到负责这个区域的快递员(返回 Program 对象)
代码示例:调用系统浏览器
public class SystemBrowserExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("调用系统浏览器");
shell.setLayout(new GridLayout(2, false));
// URL 输入框
Label urlLabel = new Label(shell, SWT.NONE);
urlLabel.setText("URL:");
Text urlText = new Text(shell, SWT.BORDER);
urlText.setText("https://www.eclipse.org");
GridData urlData = new GridData();
urlData.horizontalAlignment = GridData.FILL;
urlData.grabExcessHorizontalSpace = true;
urlText.setLayoutData(urlData);
// 文件路径输入框
Label filePathLabel = new Label(shell, SWT.NONE);
filePathLabel.setText("文件路径:");
Text filePathText = new Text(shell, SWT.BORDER);
filePathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
// 按钮面板
Composite buttonPanel = new Composite(shell, SWT.NONE);
buttonPanel.setLayout(new FillLayout(SWT.HORIZONTAL));
GridData buttonData = new GridData();
buttonData.horizontalSpan = 2;
buttonData.horizontalAlignment = GridData.FILL;
buttonPanel.setLayoutData(buttonData);
// 打开 URL 按钮
Button openUrlButton = new Button(buttonPanel, SWT.PUSH);
openUrlButton.setText("打开 URL");
openUrlButton.addListener(SWT.Selection, event -> {
String url = urlText.getText().trim();
if (url.isEmpty()) {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
messageBox.setText("警告");
messageBox.setMessage("请输入 URL!");
messageBox.open();
return;
}
try {
Program.launch(url);
System.out.println("已打开 URL:" + url);
} catch (Exception e) {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
messageBox.setText("错误");
messageBox.setMessage("无法打开 URL:" + e.getMessage());
messageBox.open();
}
});
// 打开文件按钮
Button openFileButton = new Button(buttonPanel, SWT.PUSH);
openFileButton.setText("打开文件");
openFileButton.addListener(SWT.Selection, event -> {
FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
fileDialog.setText("选择文件");
String filePath = fileDialog.open();
if (filePath != null) {
filePathText.setText(filePath);
try {
Program.launch(filePath);
System.out.println("已打开文件:" + filePath);
} catch (Exception e) {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
messageBox.setText("错误");
messageBox.setMessage("无法打开文件:" + e.getMessage());
messageBox.open();
}
}
});
// 查找浏览器按钮
Button findBrowserButton = new Button(buttonPanel, SWT.PUSH);
findBrowserButton.setText("查找浏览器");
findBrowserButton.addListener(SWT.Selection, event -> {
Program browserProgram = Program.findProgram("html");
if (browserProgram != null) {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("浏览器信息");
messageBox.setMessage("浏览器程序:" + browserProgram.getName());
messageBox.open();
} else {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
messageBox.setText("警告");
messageBox.setMessage("未找到浏览器程序");
messageBox.open();
}
});
// 查看所有程序按钮
Button listProgramsButton = new Button(buttonPanel, SWT.PUSH);
listProgramsButton.setText("查看所有程序");
listProgramsButton.addListener(SWT.Selection, event -> {
Program[] programs = Program.getPrograms();
StringBuilder sb = new StringBuilder();
sb.append("可用程序:\n");
for (Program program : programs) {
sb.append(program.getName()).append(":");
sb.append(Arrays.toString(program.getExtensions())).append("\n");
}
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("所有程序");
messageBox.setMessage(sb.toString());
messageBox.open();
});
shell.setBounds(100, 100, 500, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
13.2 系统托盘(Tray)
托盘图标
// 创建系统托盘
Tray tray = display.getSystemTray();
// 创建托盘图标
TrayItem trayItem = new TrayItem(tray, SWT.NONE);
// 设置图标
Image trayIcon = new Image(display, "path/to/icon.png");
trayItem.setImage(trayIcon);
// 设置提示文本
trayItem.setToolTipText("我的应用");
// 双击托盘图标显示窗口
trayItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
shell.setVisible(true);
shell.setMinimized(false);
shell.setActive();
}
});
费曼解释:系统托盘的原理
系统托盘 = 通知栏
- 你的应用在后台运行
- 托盘图标 = 通知
- 双击图标 = 打开应用
类比:
系统托盘 = 手机通知栏
- App 在后台运行
- 通知栏有 App 的图标
- 点击图标 = 打开 App
悬停提示
// 设置悬停提示文本
trayItem.setToolTipText("我的应用\n双击打开");
费曼解释:悬停提示的作用
悬停提示 = 名字牌
- 你把鼠标悬停在托盘图标上
- 显示名字牌(提示文本)
- 你知道这个图标是什么
类比:
悬停提示 = 快递单
- 你看到快递员
- 看快递单(提示文本)
- 你知道快递员送什么
弹出菜单
// 创建托盘图标
TrayItem trayItem = new TrayItem(tray, SWT.NONE);
trayItem.setImage(trayIcon);
// 创建弹出菜单
Menu menu = new Menu(shell, SWT.POP_UP);
// 显示菜单项
MenuItem showItem = new MenuItem(menu, SWT.PUSH);
showItem.setText("显示窗口");
showItem.addListener(SWT.Selection, event -> {
shell.setVisible(true);
shell.setMinimized(false);
shell.setActive();
});
// 隐藏菜单项
MenuItem hideItem = new MenuItem(menu, SWT.PUSH);
hideItem.setText("隐藏窗口");
hideItem.addListener(SWT.Selection, event -> {
shell.setVisible(false);
});
// 分隔线
new MenuItem(menu, SWT.SEPARATOR);
// 退出菜单项
MenuItem exitItem = new MenuItem(menu, SWT.PUSH);
exitItem.setText("退出");
exitItem.addListener(SWT.Selection, event -> {
shell.dispose();
});
// 右键点击托盘图标显示菜单
trayItem.addMenuDetectListener(event -> {
menu.setVisible(true);
});
费曼解释:托盘菜单的工作原理
托盘菜单 = 快捷菜单
- 右键点击托盘图标
- 显示快捷菜单
- 点击菜单项,执行操作
类比:
托盘菜单 = 手机长按 App 图标
- 长按 App 图标
- 显示快捷菜单
- 点击菜单项,执行操作(添加到主屏幕、卸载等)
代码示例:系统托盘
public class SystemTrayExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("系统托盘示例");
shell.setLayout(new GridLayout(1, false));
// 检查是否支持系统托盘
Tray tray = display.getSystemTray();
if (tray == null) {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
messageBox.setText("警告");
messageBox.setMessage("此系统不支持系统托盘");
messageBox.open();
shell.dispose();
display.dispose();
return;
}
// 创建托盘图标
TrayItem trayItem = new TrayItem(tray, SWT.NONE);
// 注意:实际应用中应该从文件加载图标
// Image trayIcon = new Image(display, "icon.png");
// trayItem.setImage(trayIcon);
// 设置提示文本
trayItem.setToolTipText("系统托盘示例\n右键显示菜单\n双击显示窗口");
// 创建弹出菜单
Menu menu = new Menu(shell, SWT.POP_UP);
MenuItem showItem = new MenuItem(menu, SWT.PUSH);
showItem.setText("显示窗口");
showItem.addListener(SWT.Selection, event -> {
shell.setVisible(true);
shell.setMinimized(false);
shell.setActive();
});
MenuItem hideItem = new MenuItem(menu, SWT.PUSH);
hideItem.setText("隐藏窗口");
hideItem.addListener(SWT.Selection, event -> {
shell.setVisible(false);
});
new MenuItem(menu, SWT.SEPARATOR);
MenuItem minimizeItem = new MenuItem(menu, SWT.PUSH);
minimizeItem.setText("最小化到托盘");
minimizeItem.addListener(SWT.Selection, event -> {
shell.setMinimized(true);
shell.setVisible(false);
});
MenuItem maximizeItem = new MenuItem(menu, SWT.PUSH);
maximizeItem.setText("最大化窗口");
maximizeItem.addListener(SWT.Selection, event -> {
shell.setMaximized(true);
});
new MenuItem(menu, SWT.SEPARATOR);
MenuItem aboutItem = new MenuItem(menu, SWT.PUSH);
aboutItem.setText("关于");
aboutItem.addListener(SWT.Selection, event -> {
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("关于");
messageBox.setMessage("系统托盘示例\n版本 1.0");
messageBox.open();
});
MenuItem exitItem = new MenuItem(menu, SWT.PUSH);
exitItem.setText("退出");
exitItem.addListener(SWT.Selection, event -> {
shell.dispose();
});
// 右键点击托盘图标显示菜单
trayItem.addMenuDetectListener(event -> {
menu.setVisible(true);
});
// 双击托盘图标显示窗口
trayItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
shell.setVisible(true);
shell.setMinimized(false);
shell.setActive();
}
});
// 主窗口内容
Label label = new Label(shell, SWT.WRAP | SWT.CENTER);
label.setText("这是一个系统托盘示例。\n\n最小化窗口后,托盘图标仍然存在。\n双击托盘图标可以显示窗口。\n右键托盘图标可以显示菜单。");
GridData labelData = new GridData();
labelData.horizontalAlignment = GridData.FILL;
labelData.grabExcessHorizontalSpace = true;
label.setLayoutData(labelData);
// 按钮:最小化到托盘
Button minimizeButton = new Button(shell, SWT.PUSH);
minimizeButton.setText("最小化到托盘");
minimizeButton.addListener(SWT.Selection, event -> {
shell.setMinimized(true);
shell.setVisible(false);
});
// 按钮:隐藏到托盘
Button hideButton = new Button(shell, SWT.PUSH);
hideButton.setText("隐藏到托盘");
hideButton.addListener(SWT.Selection, event -> {
shell.setVisible(false);
});
shell.setBounds(100, 100, 400, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
// 释放托盘图标
trayItem.dispose();
// trayIcon.dispose();
display.dispose();
}
}
13.3 获取系统信息
屏幕分辨率
// 获取所有显示器
Monitor[] monitors = display.getMonitors();
System.out.println("显示器数量:" + monitors.length);
for (Monitor monitor : monitors) {
Rectangle bounds = monitor.getBounds();
System.out.println("显示器边界:" + bounds);
System.out.println("宽度:" + bounds.width + ",高度:" + bounds.height);
Rectangle clientArea = monitor.getClientArea();
System.out.println("可用区域:" + clientArea);
System.out.println("可用宽度:" + clientArea.width + ",可用高度:" + clientArea.height);
}
费曼解释:显示器边界 vs 可用区域
显示器边界 = 电视边框
- 边框包括边框(显示器边界)
- 边框不包括任务栏(可用区域)
类比:
显示器边界 = 纸张大小
- 纸张大小:A4 纸(显示器边界)
- 可用大小:去掉页眉页脚(可用区域)
可用工作区
// 获取主显示器
Monitor primaryMonitor = display.getPrimaryMonitor();
Rectangle clientArea = primaryMonitor.getClientArea();
System.out.println("主显示器可用区域:" + clientArea);
System.out.println("宽度:" + clientArea.width + ",高度:" + clientArea.height);
// 计算窗口居中位置
int shellWidth = 400;
int shellHeight = 300;
int x = clientArea.x + (clientArea.width - shellWidth) / 2;
int y = clientArea.y + (clientArea.height - shellHeight) / 2;
shell.setBounds(x, y, shellWidth, shellHeight);
费曼解释:窗口居中的计算
窗口居中 = 放相册
- 墙壁(显示器)
- 相册(窗口)
- 相册居中:相册左边 = 墙壁左边 + (墙壁宽度 - 相册宽度) / 2
类比:
窗口居中 = 放海报
- 墙壁(显示器)
- 海报(窗口)
- 海报居中:海报左边 = 墙壁左边 + (墙壁宽度 - 海报宽度) / 2
系统字体
// 获取系统字体列表
FontData[] fontDatas = display.getFontList(null, true);
System.out.println("系统字体数量:" + fontDatas.length);
// 显示前 10 个字体
for (int i = 0; i < Math.min(10, fontDatas.length); i++) {
FontData fontData = fontDatas[i];
System.out.println("字体:" + fontData.getName() + ",大小:" + fontData.getHeight());
}
// 获取特定字体
FontData[] arialFonts = display.getFontList("Arial", true);
if (arialFonts.length > 0) {
System.out.println("Arial 字体数量:" + arialFonts.length);
}
费曼解释:系统字体的含义
系统字体 = 字库
- 系统安装了很多字体(字库)
- 你可以从字库里选择字体
类比:
系统字体 = 笔袋
- 笔袋里有很多笔(字体)
- 你可以从笔袋里选择笔(字体)
系统颜色
// 获取系统颜色
Color systemColor = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
System.out.println("系统背景色:" + systemColor.getRGB());
// 获取常用的系统颜色
Color[] colors = {
display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND),
display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND),
display.getSystemColor(SWT.COLOR_WIDGET_BORDER),
display.getSystemColor(SWT.COLOR_LIST_BACKGROUND),
display.getSystemColor(SWT.COLOR_LIST_FOREGROUND),
display.getSystemColor(SWT.COLOR_LIST_SELECTION),
display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT),
display.getSystemColor(SWT.COLOR_INFO_BACKGROUND),
display.getSystemColor(SWT.COLOR_INFO_FOREGROUND),
display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND),
display.getSystemColor(SWT.COLOR_TITLE_FOREGROUND),
display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT),
display.getSystemColor(SWT.COLOR_TITLE_FOREGROUND_GRADIENT),
display.getSystemColor(SWT.COLOR_WHITE),
display.getSystemColor(SWT.COLOR_BLACK),
display.getSystemColor(SWT.COLOR_RED),
display.getSystemColor(SWT.COLOR_DARK_RED),
display.getSystemColor(SWT.COLOR_GREEN),
display.getSystemColor(SWT.COLOR_DARK_GREEN),
display.getSystemColor(SWT.COLOR_YELLOW),
display.getSystemColor(SWT.COLOR_DARK_YELLOW),
display.getSystemColor(SWT.COLOR_BLUE),
display.getSystemColor(SWT.COLOR_DARK_BLUE),
display.getSystemColor(SWT.COLOR_MAGENTA),
display.getSystemColor(SWT.COLOR_CYAN),
display.getSystemColor(SWT.COLOR_GRAY)
};
// 显示所有颜色
for (int i = 0; i < colors.length; i++) {
System.out.println("颜色 " + i + ":" + colors[i].getRGB());
}
费曼解释:系统颜色的用途
系统颜色 = 系统调色板
- 系统预定义了很多颜色(调色板)
- 你可以从调色板选择颜色
- 使用系统颜色,应用风格与系统一致
类比:
系统颜色 = 餐厅调料
- 餐厅准备了很多调料(系统颜色)
- 你可以从调料里选择(使用系统颜色)
- 使用餐厅调料,味道与餐厅一致
代码示例:获取系统信息
public class SystemInfoExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("系统信息示例");
shell.setLayout(new GridLayout(1, false));
// 文本框:显示系统信息
Text infoText = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
GridData textData = new GridData();
textData.horizontalAlignment = GridData.FILL;
textData.verticalAlignment = GridData.FILL;
textData.grabExcessHorizontalSpace = true;
textData.grabExcessVerticalSpace = true;
textData.heightHint = 300;
infoText.setLayoutData(textData);
// 按钮:获取显示器信息
Button monitorButton = new Button(shell, SWT.PUSH);
monitorButton.setText("获取显示器信息");
monitorButton.addListener(SWT.Selection, event -> {
StringBuilder sb = new StringBuilder();
sb.append("显示器信息:\n\n");
Monitor[] monitors = display.getMonitors();
sb.append("显示器数量:").append(monitors.length).append("\n\n");
for (int i = 0; i < monitors.length; i++) {
Monitor monitor = monitors[i];
Rectangle bounds = monitor.getBounds();
Rectangle clientArea = monitor.getClientArea();
sb.append("显示器 ").append(i + 1).append(":\n");
sb.append(" 边界:").append(bounds).append("\n");
sb.append(" 可用区域:").append(clientArea).append("\n");
sb.append(" 是否是主显示器:").append(monitor.equals(display.getPrimaryMonitor())).append("\n\n");
}
infoText.setText(sb.toString());
});
// 按钮:获取系统字体
Button fontButton = new Button(shell, SWT.PUSH);
fontButton.setText("获取系统字体");
fontButton.addListener(SWT.Selection, event -> {
StringBuilder sb = new StringBuilder();
sb.append("系统字体:\n\n");
FontData[] fontDatas = display.getFontList(null, true);
sb.append("字体总数:").append(fontDatas.length).append("\n\n");
sb.append("前 20 个字体:\n");
for (int i = 0; i < Math.min(20, fontDatas.length); i++) {
FontData fontData = fontDatas[i];
sb.append(i + 1).append(". ")
.append(fontData.getName())
.append(" (大小: ").append(fontData.getHeight()).append(")\n");
}
infoText.setText(sb.toString());
});
// 按钮:获取系统颜色
Button colorButton = new Button(shell, SWT.PUSH);
colorButton.setText("获取系统颜色");
colorButton.addListener(SWT.Selection, event -> {
StringBuilder sb = new StringBuilder();
sb.append("系统颜色:\n\n");
Color[] colors = {
display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND),
display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND),
display.getSystemColor(SWT.COLOR_WIDGET_BORDER),
display.getSystemColor(SWT.COLOR_LIST_BACKGROUND),
display.getSystemColor(SWT.COLOR_LIST_FOREGROUND),
display.getSystemColor(SWT.COLOR_LIST_SELECTION),
display.getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT),
display.getSystemColor(SWT.COLOR_INFO_BACKGROUND),
display.getSystemColor(SWT.COLOR_INFO_FOREGROUND),
display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND),
display.getSystemColor(SWT.COLOR_TITLE_FOREGROUND),
display.getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT),
display.getSystemColor(SWT.COLOR_TITLE_FOREGROUND_GRADIENT),
display.getSystemColor(SWT.COLOR_WHITE),
display.getSystemColor(SWT.COLOR_BLACK),
display.getSystemColor(SWT.COLOR_RED),
display.getSystemColor(SWT.COLOR_DARK_RED),
display.getSystemColor(SWT.COLOR_GREEN),
display.getSystemColor(SWT.COLOR_DARK_GREEN),
display.getSystemColor(SWT.COLOR_YELLOW),
display.getSystemColor(SWT.COLOR_DARK_YELLOW),
display.getSystemColor(SWT.COLOR_BLUE),
display.getSystemColor(SWT.COLOR_DARK_BLUE),
display.getSystemColor(SWT.COLOR_MAGENTA),
display.getSystemColor(SWT.COLOR_CYAN),
display.getSystemColor(SWT.COLOR_GRAY)
};
String[] colorNames = {
"WIDGET_BACKGROUND", "WIDGET_FOREGROUND", "WIDGET_BORDER",
"LIST_BACKGROUND", "LIST_FOREGROUND", "LIST_SELECTION", "LIST_SELECTION_TEXT",
"INFO_BACKGROUND", "INFO_FOREGROUND",
"TITLE_BACKGROUND", "TITLE_FOREGROUND", "TITLE_BACKGROUND_GRADIENT", "TITLE_FOREGROUND_GRADIENT",
"WHITE", "BLACK", "RED", "DARK_RED", "GREEN", "DARK_GREEN",
"YELLOW", "DARK_YELLOW", "BLUE", "DARK_BLUE", "MAGENTA", "CYAN", "GRAY"
};
for (int i = 0; i < colors.length; i++) {
RGB rgb = colors[i].getRGB();
sb.append(colorNames[i]).append(":")
.append("R=").append(rgb.red)
.append(", G=").append(rgb.green)
.append(", B=").append(rgb.blue)
.append("\n");
}
infoText.setText(sb.toString());
});
shell.setBounds(100, 100, 600, 400);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
13.4 平台特定的注意事项
Windows 的特殊处理
// 检测是否是 Windows
boolean isWindows = Platform.getWS() == Platform.WS_WIN32;
if (isWindows) {
System.out.println("运行在 Windows 平台");
// Windows 特定的处理
System.setProperty("sun.java2d.noddraw", "true"); // 禁用 DDraw,提高性能
// 设置窗口透明度(仅 Windows 10+)
if (Platform.isWindows10OrLater()) {
shell.setAlpha(200); // 0-255,255 = 不透明
}
}
费曼解释:平台特定的必要性
平台特定 = 个性化服务
- Windows 有 Windows 的特点
- Mac 有 Mac 的特点
- Linux 有 Linux 的特点
- 针对不同平台,提供不同的服务
类比:
平台特定 = 客户定制
- 客户 A(Windows)喜欢红色
- 客户 B(Mac)喜欢蓝色
- 客户 C(Linux)喜欢绿色
- 针对不同客户,提供不同的颜色
Mac 的菜单栏规则
// 检测是否是 Mac
boolean isMac = Platform.getWS() == Platform.WS_COCOA;
if (isMac) {
System.out.println("运行在 Mac 平台");
// Mac 特定的处理
System.setProperty("apple.laf.useScreenMenuBar", "true"); // 菜单栏在屏幕顶部
// 设置应用名称(会在 Dock 显示)
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "我的应用");
// 设置 Dock 图标(注意:需要从文件加载)
// Image dockIcon = new Image(display, "icon.png");
// shell.setImage(dockIcon);
// Mac 的 Cmd+Q 退出快捷键
shell.addListener(SWT.Close, event -> {
// Mac 点击窗口关闭按钮,不是退出应用
shell.setVisible(false);
});
}
费曼解释:Mac 菜单栏的规则
Mac 菜单栏 = 屋顶大招牌
- Mac 的菜单栏在屏幕顶部
- 所有应用共享菜单栏
- 菜单栏显示当前应用的菜单
类比:
Mac 菜单栏 = 商场导购图
- 所有店铺共享导购图(菜单栏)
- 当前店铺显示在导购图上(当前应用的菜单)
- 切换店铺,导购图切换(切换应用,菜单栏切换)
Linux 的主题适配
// 检测是否是 Linux
boolean isLinux = Platform.getWS() == Platform.WS_GTK;
if (isLinux) {
System.out.println("运行在 Linux 平台");
// Linux 特定的处理
// SWT 会自动使用 GTK 主题
// 检测桌面环境
String desktop = System.getenv("XDG_CURRENT_DESKTOP");
if (desktop != null) {
System.out.println("桌面环境:" + desktop);
if (desktop.contains("GNOME")) {
System.out.println("使用 GNOME 主题");
} else if (desktop.contains("KDE")) {
System.out.println("使用 KDE 主题");
} else if (desktop.contains("XFCE")) {
System.out.println("使用 XFCE 主题");
}
}
}
费曼解释:Linux 主题的适配
Linux 主题 = 装修风格
- GNOME 有 GNOME 的装修风格
- KDE 有 KDE 的装修风格
- XFCE 有 XFCE 的装修风格
- SWT 自动使用桌面的装修风格
类比:
Linux 主题 = 家具风格
- 现代风格(GNOME 主题)
- 古典风格(KDE 主题)
- 简约风格(XFCE 主题)
- 应用自动使用桌面的家具风格
代码示例:平台特定处理
public class PlatformSpecificExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("平台特定处理");
shell.setLayout(new GridLayout(1, false));
// 平台特定处理
String platform = "";
if (Platform.getWS() == Platform.WS_WIN32) {
platform = "Windows";
System.setProperty("sun.java2d.noddraw", "true");
} else if (Platform.getWS() == Platform.WS_COCOA) {
platform = "Mac";
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "平台特定示例");
} else if (Platform.getWS() == Platform.WS_GTK) {
platform = "Linux";
} else {
platform = "未知";
}
// 显示平台信息
Label platformLabel = new Label(shell, SWT.WRAP | SWT.CENTER);
platformLabel.setText("当前平台:" + platform + "\nSWT 会自动适配平台的特性。");
GridData labelData = new GridData();
labelData.horizontalAlignment = GridData.FILL;
labelData.grabExcessHorizontalSpace = true;
platformLabel.setLayoutData(labelData);
// 按钮面板
Composite buttonPanel = new Composite(shell, SWT.NONE);
buttonPanel.setLayout(new FillLayout(SWT.HORIZONTAL));
// 显示平台信息按钮
Button showButton = new Button(buttonPanel, SWT.PUSH);
showButton.setText("显示平台信息");
showButton.addListener(SWT.Selection, event -> {
StringBuilder sb = new StringBuilder();
sb.append("平台信息:\n\n");
sb.append("WS:").append(Platform.getWS()).append("\n");
sb.append("Java 版本:").append(System.getProperty("java.version")).append("\n");
sb.append("OS 名称:").append(System.getProperty("os.name")).append("\n");
sb.append("OS 版本:").append(System.getProperty("os.version")).append("\n");
sb.append("OS 架构:").append(System.getProperty("os.arch")).append("\n");
if (platform.equals("Linux")) {
String desktop = System.getenv("XDG_CURRENT_DESKTOP");
sb.append("桌面环境:").append(desktop != null ? desktop : "未知").append("\n");
}
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("平台信息");
messageBox.setMessage(sb.toString());
messageBox.open();
});
// 显示系统颜色按钮
Button colorButton = new Button(buttonPanel, SWT.PUSH);
colorButton.setText("显示系统颜色");
colorButton.addListener(SWT.Selection, event -> {
Color backgroundColor = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
Color foregroundColor = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
Color borderColor = display.getSystemColor(SWT.COLOR_WIDGET_BORDER);
shell.setBackground(backgroundColor);
platformLabel.setForeground(foregroundColor);
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("系统颜色");
messageBox.setMessage("背景色:" + backgroundColor.getRGB() + "\n前景色:" + foregroundColor.getRGB() + "\n边框色:" + borderColor.getRGB());
messageBox.open();
});
// 显示系统字体按钮
Button fontButton = new Button(buttonPanel, SWT.PUSH);
fontButton.setText("显示系统字体");
fontButton.addListener(SWT.Selection, event -> {
FontData[] fontDatas = display.getFontList(null, true);
StringBuilder sb = new StringBuilder();
sb.append("系统字体总数:").append(fontDatas.length).append("\n\n");
sb.append("前 10 个字体:\n");
for (int i = 0; i < Math.min(10, fontDatas.length); i++) {
FontData fontData = fontDatas[i];
sb.append(i + 1).append(". ").append(fontData.getName()).append("\n");
}
MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
messageBox.setText("系统字体");
messageBox.setMessage(sb.toString());
messageBox.open();
});
shell.setBounds(100, 100, 500, 200);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
13.5 本章小结
系统集成总结
| 功能 | 作用 | 类比 |
|---|---|---|
| Program.launch() | 调用系统默认程序 | 快递配送 |
| TrayItem | 系统托盘图标 | 通知栏 |
| Monitor | 获取显示器信息 | 电视边框 |
| FontData | 获取系统字体 | 字库 |
| 系统颜色 | 获取系统颜色 | 系统调色板 |
费曼测试:你能解释清楚吗?
- Program.launch() 的作用?
- 调用系统默认程序打开 URL 或文件
- 系统托盘的用途?
- 在后台运行应用,托盘图标显示通知
- 如何获取主显示器的可用区域?
- display.getPrimaryMonitor().getClientArea()
- 为什么需要平台特定的处理?
- 不同平台有不同的特性和规则
下一章预告
现在你已经掌握了 SWT 与操作系统的深度集成,
可以调用系统浏览器、系统托盘等功能,
让应用更好地利用系统特性。
下一章,我们将学习国际化(i18n),
让应用支持多种语言,
服务全球用户。
练习题:
- 创建一个应用,包含一个文本框和一个按钮,点击按钮在默认浏览器中打开文本框中的 URL。
- 创建一个应用,包含系统托盘图标,双击显示窗口,右键显示菜单(显示/隐藏/退出)。
- 创建一个应用,获取并显示所有显示器的信息(边界、可用区域)。
(提示:使用 Program.launch() 打开 URL,使用 TrayItem 创建托盘图标,使用 display.getMonitors() 获取显示器)