第8章:对话框与消息框 - 用户友好的沟通

第8章:对话框与消息框 - 用户友好的沟通


作者:步子哥 (steper@foxmail.com)


8.1 消息框(MessageBox)

信息、警告、错误、确认

// 信息消息框
MessageBox infoBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
infoBox.setText("信息");
infoBox.setMessage("这是一条信息消息。");
int result = infoBox.open();
System.out.println("用户点击了:" + (result == SWT.OK ? "确定" : "其他"));

// 警告消息框
MessageBox warningBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
warningBox.setText("警告");
warningBox.setMessage("这是一条警告消息。");
warningBox.open();

// 错误消息框
MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
errorBox.setText("错误");
errorBox.setMessage("这是一条错误消息。");
errorBox.open();

// 确认消息框
MessageBox confirmBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
confirmBox.setText("确认");
confirmBox.setMessage("你确定要删除这个文件吗?");
int confirmResult = confirmBox.open();
if (confirmResult == SWT.YES) {
    System.out.println("用户确认删除");
} else if (confirmResult == SWT.NO) {
    System.out.println("用户取消删除");
}

费曼解释:四种消息框的区别

信息 = 通知
- 告诉你一件事
- 你知道了就行

警告 = 预警
- 提醒你注意
- 但不严重

错误 = 故障
- 出错了
- 需要处理

确认 = 询问
- 问你同不同意
- 你做决定

类比:
信息 = 邮件通知
- 收到新邮件
- 告诉你一声

警告 = 天气预报
- 明天有雨
- 带把伞

错误 = 红灯
- 前面有障碍
- 停车检查

确认 = 订票确认
- 你要订票吗?
- 确定或取消

自定义按钮文本

// 自定义按钮文本的消息框
MessageBox messageBox = new MessageBox(shell, SWT.ICON_QUESTION);
messageBox.setText("保存文件");
messageBox.setMessage("文件已修改,是否保存?");

// 注意:SWT 的 MessageBox 不支持自定义按钮文本
// 如果需要自定义按钮文本,需要创建自定义对话框(见 8.6 节)

费曼解释:为什么不能自定义按钮文本?

MessageBox = 预制模板
- 提供常用的按钮(确定、取消、是、否...)
- 不支持自定义按钮
- 简单快速

类比:
MessageBox = 快餐店
- 提供固定的套餐
- 不能随意搭配
- 快速方便

自定义对话框 = 自助餐厅
- 想吃什么选什么
- 可以随意搭配
- 自由度高

获取用户选择

// 确认消息框
MessageBox confirmBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
confirmBox.setText("确认操作");
confirmBox.setMessage("你确定要执行此操作吗?");
int result = confirmBox.open();

switch (result) {
    case SWT.YES:
        System.out.println("用户选择了:是");
        break;
    case SWT.NO:
        System.out.println("用户选择了:否");
        break;
    case SWT.CANCEL:
        System.out.println("用户选择了:取消");
        break;
}

费曼解释:获取用户选择的逻辑

获取用户选择 = 做选择题
- 出题人:你
- 答题人:用户
- 收卷人:open() 方法
- 试卷:result 变量

类比:
获取用户选择 = 餐厅点菜
- 餐厅服务员:你
- 顾客:用户
- 菜单:按钮选项
- 订单:result 变量

代码示例:消息框演示

public class MessageBoxExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("MessageBox 示例");
        shell.setLayout(new GridLayout(2, false));
        
        // 信息按钮
        Button infoButton = new Button(shell, SWT.PUSH);
        infoButton.setText("信息");
        infoButton.addListener(SWT.Selection, event -> {
            MessageBox infoBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
            infoBox.setText("信息");
            infoBox.setMessage("这是一条信息消息。\n操作已成功完成。");
            infoBox.open();
        });
        
        // 警告按钮
        Button warningButton = new Button(shell, SWT.PUSH);
        warningButton.setText("警告");
        warningButton.addListener(SWT.Selection, event -> {
            MessageBox warningBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK);
            warningBox.setText("警告");
            warningBox.setMessage("这是一条警告消息。\n请谨慎操作。");
            warningBox.open();
        });
        
        // 错误按钮
        Button errorButton = new Button(shell, SWT.PUSH);
        errorButton.setText("错误");
        errorButton.addListener(SWT.Selection, event -> {
            MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
            errorBox.setText("错误");
            errorBox.setMessage("这是一条错误消息。\n操作失败,请重试。");
            errorBox.open();
        });
        
        // 确认按钮
        Button confirmButton = new Button(shell, SWT.PUSH);
        confirmButton.setText("确认");
        confirmButton.addListener(SWT.Selection, event -> {
            MessageBox confirmBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
            confirmBox.setText("确认");
            confirmBox.setMessage("你确定要执行此操作吗?");
            int result = confirmBox.open();
            if (result == SWT.YES) {
                System.out.println("用户确认");
            } else {
                System.out.println("用户取消");
            }
        });
        
        // 三选项按钮
        Button threeButton = new Button(shell, SWT.PUSH);
        threeButton.setText("三选项");
        threeButton.addListener(SWT.Selection, event -> {
            MessageBox threeBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
            threeBox.setText("三选项");
            threeBox.setMessage("文件已修改,是否保存?");
            int result = threeBox.open();
            switch (result) {
                case SWT.YES:
                    System.out.println("用户选择:保存");
                    break;
                case SWT.NO:
                    System.out.println("用户选择:不保存");
                    break;
                case SWT.CANCEL:
                    System.out.println("用户选择:取消");
                    break;
            }
        });
        
        // 重试取消按钮
        Button retryButton = new Button(shell, SWT.PUSH);
        retryButton.setText("重试/取消");
        retryButton.addListener(SWT.Selection, event -> {
            MessageBox retryBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.RETRY | SWT.CANCEL);
            retryBox.setText("重试");
            retryBox.setMessage("连接失败,是否重试?");
            int result = retryBox.open();
            switch (result) {
                case SWT.RETRY:
                    System.out.println("用户选择:重试");
                    break;
                case SWT.CANCEL:
                    System.out.println("用户选择:取消");
                    break;
            }
        });
        
        // 中止重试忽略按钮
        Button abortButton = new Button(shell, SWT.PUSH);
        abortButton.setText("中止/重试/忽略");
        abortButton.addListener(SWT.Selection, event -> {
            MessageBox abortBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.ABORT | SWT.RETRY | SWT.IGNORE);
            abortBox.setText("错误");
            abortBox.setMessage("发生错误,请选择操作。");
            int result = abortBox.open();
            switch (result) {
                case SWT.ABORT:
                    System.out.println("用户选择:中止");
                    break;
                case SWT.RETRY:
                    System.out.println("用户选择:重试");
                    break;
                case SWT.IGNORE:
                    System.out.println("用户选择:忽略");
                    break;
            }
        });
        
        shell.setBounds(100, 100, 400, 200);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        display.dispose();
    }
}

8.2 文件对话框(FileDialog)

打开文件 vs 保存文件

// 打开文件对话框
FileDialog openFileDialog = new FileDialog(shell, SWT.OPEN);
openFileDialog.setText("打开文件");
openFileDialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
openFileDialog.setFilterNames(new String[]{"文本文件 (*.txt)", "所有文件 (*.*)"});
openFileDialog.setFilterPath("C:\\");
openFileDialog.setFileName("");
String selectedFile = openFileDialog.open();
if (selectedFile != null) {
    System.out.println("选择的文件:" + selectedFile);
}

// 保存文件对话框
FileDialog saveFileDialog = new FileDialog(shell, SWT.SAVE);
saveFileDialog.setText("保存文件");
saveFileDialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
saveFileDialog.setFilterNames(new String[]{"文本文件 (*.txt)", "所有文件 (*.*)"});
saveFileDialog.setFilterPath("C:\\");
saveFileDialog.setFileName("newfile.txt");
String saveFile = saveFileDialog.open();
if (saveFile != null) {
    System.out.println("保存到:" + saveFile);
}

费曼解释:打开文件 vs 保存文件

打开文件 = 翻开书
- 书已经存在
- 你翻开阅读
- 不修改内容

保存文件 = 写日记
- 日记可能不存在
- 你创建并写入
- 保存你的内容

类比:
打开文件 = 打开冰箱
- 冰箱里有食物
- 你拿出来吃
- 不改变冰箱的内容

保存文件 = 放回食物
- 你吃完食物
- 放回冰箱
- 改变冰箱的内容

过滤文件类型

FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
fileDialog.setText("打开文件");

// 设置文件过滤器
fileDialog.setFilterExtensions(new String[]{
    "*.txt",      // 文本文件
    "*.jpg",      // JPEG 图片
    "*.png",      // PNG 图片
    "*.*"         // 所有文件
});

// 设置过滤器名称
fileDialog.setFilterNames(new String[]{
    "文本文件 (*.txt)",
    "JPEG 图片 (*.jpg)",
    "PNG 图片 (*.png)",
    "所有文件 (*.*)"
});

// 设置默认过滤器(第 3 个,索引从 0 开始)
fileDialog.setFilterIndex(2);

String selectedFile = fileDialog.open();
if (selectedFile != null) {
    System.out.println("选择的文件:" + selectedFile);
}

费曼解释:文件过滤器的作用

文件过滤器 = 分类篮子
- 食物放在不同的篮子
- 水果篮、蔬菜篮、肉类篮...
- 你想吃什么,去对应的篮子

类比:
文件过滤器 = 餐厅菜单
- 分类清晰:前菜、主菜、甜点
- 每一类列出具体的菜品
- 你想吃主菜,看主菜菜单

多文件选择

// 多文件选择对话框
FileDialog multiFileDialog = new FileDialog(shell, SWT.OPEN | SWT.MULTI);
multiFileDialog.setText("打开文件");
multiFileDialog.setFilterExtensions(new String[]{"*.txt", "*.*"});

String selectedFiles = multiFileDialog.open();
if (selectedFiles != null) {
    // 获取选择的文件
    String[] fileNames = multiFileDialog.getFileNames();
    String filterPath = multiFileDialog.getFilterPath();
    
    System.out.println("选择的文件:");
    for (String fileName : fileNames) {
        System.out.println(filterPath + File.separator + fileName);
    }
}

费曼解释:多文件选择的原理

多文件选择 = 批量购买
- 一次选多个商品
- 放进购物车
- 统一结账

类比:
多文件选择 = 选课系统
- 一次选多门课程
- 添加到选课表
- 统一提交

代码示例:文件对话框演示

public class FileDialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("FileDialog 示例");
        shell.setLayout(new GridLayout(1, false));
        
        // 文本框:显示选择的文件
        Text text = 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 = 200;
        text.setLayoutData(textData);
        
        // 按钮面板
        Composite buttonPanel = new Composite(shell, SWT.NONE);
        buttonPanel.setLayout(new FillLayout(SWT.HORIZONTAL));
        
        // 打开文件按钮
        Button openButton = new Button(buttonPanel, SWT.PUSH);
        openButton.setText("打开文件");
        openButton.addListener(SWT.Selection, event -> {
            FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
            fileDialog.setText("打开文件");
            fileDialog.setFilterExtensions(new String[]{"*.txt", "*.java", "*.*"});
            fileDialog.setFilterNames(new String[]{"文本文件 (*.txt)", "Java 文件 (*.java)", "所有文件 (*.*)"});
            fileDialog.setFilterPath(System.getProperty("user.dir"));
            
            String selectedFile = fileDialog.open();
            if (selectedFile != null) {
                text.append("打开文件:" + selectedFile + "\n");
                
                try {
                    String content = new String(Files.readAllBytes(Paths.get(selectedFile)));
                    text.append("内容:\n" + content + "\n");
                } catch (IOException e) {
                    text.append("读取失败:" + e.getMessage() + "\n");
                }
            }
        });
        
        // 保存文件按钮
        Button saveButton = new Button(buttonPanel, SWT.PUSH);
        saveButton.setText("保存文件");
        saveButton.addListener(SWT.Selection, event -> {
            FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
            fileDialog.setText("保存文件");
            fileDialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
            fileDialog.setFilterNames(new String[]{"文本文件 (*.txt)", "所有文件 (*.*)"});
            fileDialog.setFilterPath(System.getProperty("user.dir"));
            fileDialog.setFileName("newfile.txt");
            
            String saveFile = fileDialog.open();
            if (saveFile != null) {
                text.append("保存文件:" + saveFile + "\n");
                
                try {
                    Files.write(Paths.get(saveFile), text.getText().getBytes());
                    text.append("保存成功!\n");
                } catch (IOException e) {
                    text.append("保存失败:" + e.getMessage() + "\n");
                }
            }
        });
        
        // 多文件选择按钮
        Button multiButton = new Button(buttonPanel, SWT.PUSH);
        multiButton.setText("多文件选择");
        multiButton.addListener(SWT.Selection, event -> {
            FileDialog fileDialog = new FileDialog(shell, SWT.OPEN | SWT.MULTI);
            fileDialog.setText("打开文件");
            fileDialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
            fileDialog.setFilterPath(System.getProperty("user.dir"));
            
            String selectedFiles = fileDialog.open();
            if (selectedFiles != null) {
                String[] fileNames = fileDialog.getFileNames();
                String filterPath = fileDialog.getFilterPath();
                
                text.append("选择了 " + fileNames.length + " 个文件:\n");
                for (String fileName : fileNames) {
                    String fullPath = filterPath + File.separator + fileName;
                    text.append("  " + fullPath + "\n");
                }
            }
        });
        
        shell.setBounds(100, 100, 600, 400);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        display.dispose();
    }
}

8.3 目录对话框(DirectoryDialog)

选择目录

DirectoryDialog directoryDialog = new DirectoryDialog(shell);
directoryDialog.setText("选择目录");
directoryDialog.setFilterPath("C:\\");
String selectedDirectory = directoryDialog.open();
if (selectedDirectory != null) {
    System.out.println("选择的目录:" + selectedDirectory);
}

费曼解释:目录对话框的用途

目录对话框 = 选择房间
- 一栋楼有多个房间
- 你选择一个房间
- 进入房间

类比:
目录对话框 = 停车场
- 停车场有多个车位
- 你选择一个车位
- 停车

代码示例:目录对话框

public class DirectoryDialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("DirectoryDialog 示例");
        shell.setLayout(new GridLayout(1, false));
        
        // 文本框:显示选择的目录
        Text text = 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 = 200;
        text.setLayoutData(textData);
        
        // 按钮:选择目录
        Button selectButton = new Button(shell, SWT.PUSH);
        selectButton.setText("选择目录");
        selectButton.addListener(SWT.Selection, event -> {
            DirectoryDialog directoryDialog = new DirectoryDialog(shell);
            directoryDialog.setText("选择目录");
            directoryDialog.setMessage("请选择一个目录:");
            directoryDialog.setFilterPath(System.getProperty("user.home"));
            
            String selectedDirectory = directoryDialog.open();
            if (selectedDirectory != null) {
                text.append("选择的目录:" + selectedDirectory + "\n");
                
                // 列出目录下的文件
                try {
                    File directory = new File(selectedDirectory);
                    File[] files = directory.listFiles();
                    if (files != null) {
                        text.append("目录下的文件:\n");
                        for (File file : files) {
                            if (file.isDirectory()) {
                                text.append("  [目录] " + file.getName() + "\n");
                            } else {
                                text.append("  [文件] " + file.getName() + "\n");
                            }
                        }
                    }
                } catch (Exception e) {
                    text.append("读取失败:" + e.getMessage() + "\n");
                }
            }
        });
        
        shell.setBounds(100, 100, 600, 300);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        display.dispose();
    }
}

8.4 颜色对话框(ColorDialog)

选择颜色

ColorDialog colorDialog = new ColorDialog(shell);
colorDialog.setText("选择颜色");
colorDialog.setRGB(new RGB(255, 0, 0));  // 设置默认颜色(红色)
RGB rgb = colorDialog.open();
if (rgb != null) {
    System.out.println("选择的颜色:R=" + rgb.red + ", G=" + rgb.green + ", B=" + rgb.blue);
    
    Color color = new Color(display, rgb);
    // 使用颜色...
    color.dispose();  // 别忘了释放
}

费曼解释:颜色对话框的原理

颜色对话框 = 调色板
- 有很多颜色
- 你选择一个
- 拿到颜色

类比:
颜色对话框 = 彩笔盒
- 有很多颜色的彩笔
- 你选择一支
- 画画

自定义颜色

ColorDialog colorDialog = new ColorDialog(shell);
colorDialog.setText("选择颜色");

// 设置自定义颜色
RGB[] rgbColors = new RGB[]{
    new RGB(255, 0, 0),      // 红色
    new RGB(0, 255, 0),      // 绿色
    new RGB(0, 0, 255),      // 蓝色
    new RGB(255, 255, 0),    // 黄色
    new RGB(255, 0, 255),    // 紫色
    new RGB(0, 255, 255),    // 青色
    new RGB(255, 128, 0),    // 橙色
    new RGB(128, 128, 128),  // 灰色
    new RGB(0, 0, 0),        // 黑色
    new RGB(255, 255, 255)   // 白色
};
colorDialog.setRGBs(rgbColors);

RGB rgb = colorDialog.open();
if (rgb != null) {
    System.out.println("选择的颜色:R=" + rgb.red + ", G=" + rgb.green + ", B=" + rgb.blue);
}

费曼解释:自定义颜色的好处

自定义颜色 = 个人调色板
- 你常用的颜色
- 预先保存
- 快速选择

类比:
自定义颜色 = 常用电话号码
- 你常用的联系人
- 预先保存
- 快速拨打

代码示例:颜色对话框

public class ColorDialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("ColorDialog 示例");
        shell.setLayout(new GridLayout(2, false));
        
        // 画布
        Canvas canvas = new Canvas(shell, SWT.BORDER);
        GridData canvasData = new GridData();
        canvasData.horizontalAlignment = GridData.FILL;
        canvasData.verticalAlignment = GridData.FILL;
        canvasData.grabExcessHorizontalSpace = true;
        canvasData.grabExcessVerticalSpace = true;
        canvasData.widthHint = 200;
        canvasData.heightHint = 200;
        canvas.setLayoutData(canvasData);
        
        // 初始颜色:白色
        RGB currentRGB = new RGB(255, 255, 255);
        
        // 绘制
        canvas.addPaintListener(event -> {
            GC gc = event.gc;
            gc.setBackground(new Color(display, currentRGB));
            gc.fillRectangle(canvas.getClientArea());
        });
        
        // 按钮面板
        Composite buttonPanel = new Composite(shell, SWT.NONE);
        buttonPanel.setLayout(new GridLayout(1, false));
        
        // 选择颜色按钮
        Button selectButton = new Button(buttonPanel, SWT.PUSH);
        selectButton.setText("选择颜色");
        selectButton.addListener(SWT.Selection, event -> {
            ColorDialog colorDialog = new ColorDialog(shell);
            colorDialog.setText("选择颜色");
            colorDialog.setRGB(currentRGB);  // 设置当前颜色
            
            // 设置自定义颜色
            RGB[] customColors = new RGB[]{
                new RGB(255, 0, 0),
                new RGB(0, 255, 0),
                new RGB(0, 0, 255),
                new RGB(255, 255, 0),
                new RGB(255, 0, 255),
                new RGB(0, 255, 255),
                new RGB(255, 128, 0),
                new RGB(128, 128, 128),
                new RGB(0, 0, 0),
                new RGB(255, 255, 255)
            };
            colorDialog.setRGBs(customColors);
            
            RGB rgb = colorDialog.open();
            if (rgb != null) {
                currentRGB = rgb;
                canvas.redraw();
                System.out.println("选择的颜色:R=" + rgb.red + ", G=" + rgb.green + ", B=" + rgb.blue);
            }
        });
        
        // 预设颜色按钮
        Button presetButton = new Button(buttonPanel, SWT.PUSH);
        presetButton.setText("预设颜色");
        presetButton.addListener(SWT.Selection, event -> {
            ColorDialog colorDialog = new ColorDialog(shell);
            colorDialog.setText("选择预设颜色");
            
            // 随机生成一个颜色
            Random random = new Random();
            RGB randomRGB = new RGB(random.nextInt(256), random.nextInt(256), random.nextInt(256));
            colorDialog.setRGB(randomRGB);
            
            RGB rgb = colorDialog.open();
            if (rgb != null) {
                currentRGB = rgb;
                canvas.redraw();
            }
        });
        
        shell.setBounds(100, 100, 400, 300);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        display.dispose();
    }
}

8.5 字体对话框(FontDialog)

选择字体、大小、样式

FontDialog fontDialog = new FontDialog(shell);
fontDialog.setText("选择字体");
FontData defaultFontData = new FontData("Arial", 12, SWT.NORMAL);
fontDialog.setFontList(new FontData[]{defaultFontData});
FontData fontData = fontDialog.open();
if (fontData != null) {
    System.out.println("选择的字体:" + fontData.getName());
    System.out.println("字体大小:" + fontData.getHeight());
    System.out.println("字体样式:" + fontData.getStyle());
    
    Font font = new Font(display, fontData);
    // 使用字体...
    font.dispose();  // 别忘了释放
}

费曼解释:字体对话框的参数

字体对话框 = 试衣间
- 有很多衣服(字体)
- 你选择一件(字体)
- 试穿一下(查看效果)

类比:
字体对话框 = 字体预览
- 输入一段文字
- 选择字体、大小、样式
- 预览效果

代码示例:字体对话框

public class FontDialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("FontDialog 示例");
        shell.setLayout(new GridLayout(1, false));
        
        // 文本框
        Text text = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
        GridData textData = new GridData();
        textData.horizontalAlignment = GridData.FILL;
        textData.verticalAlignment = GridData.FILL;
        textData.grabExcessHorizontalSpace = true;
        textData.grabExcessVerticalSpace = true;
        textData.heightHint = 200;
        text.setLayoutData(textData);
        text.setText("这是一段示例文本。\n点击"选择字体"按钮,可以改变文本的字体、大小和样式。\n字体对话框提供了丰富的选择。");
        
        // 初始字体
        FontData currentFontData = new FontData("Arial", 14, SWT.NORMAL);
        Font currentFont = new Font(display, currentFontData);
        text.setFont(currentFont);
        
        // 按钮:选择字体
        Button selectButton = new Button(shell, SWT.PUSH);
        selectButton.setText("选择字体");
        selectButton.addListener(SWT.Selection, event -> {
            FontDialog fontDialog = new FontDialog(shell);
            fontDialog.setText("选择字体");
            fontDialog.setFontList(new FontData[]{currentFontData});
            
            FontData fontData = fontDialog.open();
            if (fontData != null) {
                // 释放旧字体
                currentFont.dispose();
                
                // 保存新字体
                currentFontData = fontData;
                currentFont = new Font(display, fontData);
                
                // 应用新字体
                text.setFont(currentFont);
                
                System.out.println("字体名称:" + fontData.getName());
                System.out.println("字体大小:" + fontData.getHeight());
                System.out.println("字体样式:" + fontData.getStyle());
            }
        });
        
        // 按钮:重置字体
        Button resetButton = new Button(shell, SWT.PUSH);
        resetButton.setText("重置字体");
        resetButton.addListener(SWT.Selection, event -> {
            // 释放旧字体
            currentFont.dispose();
            
            // 重置为默认字体
            currentFontData = new FontData("Arial", 14, SWT.NORMAL);
            currentFont = new Font(display, currentFontData);
            text.setFont(currentFont);
        });
        
        shell.setBounds(100, 100, 600, 400);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        // 释放字体
        currentFont.dispose();
        display.dispose();
    }
}

8.6 自定义对话框

继承 Dialog 类

public class CustomDialog extends Dialog {
    private String result;
    
    public CustomDialog(Shell parentShell) {
        super(parentShell);
    }
    
    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText("自定义对话框");
    }
    
    @Override
    protected Control createDialogArea(Composite parent) {
        Composite composite = (Composite) super.createDialogArea(parent);
        composite.setLayout(new GridLayout(2, false));
        
        new Label(composite, SWT.NONE).setText("请输入内容:");
        
        Text text = new Text(composite, SWT.BORDER);
        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        
        Button okButton = new Button(composite, SWT.PUSH);
        okButton.setText("确定");
        okButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                result = text.getText();
                close();
            }
        });
        
        Button cancelButton = new Button(composite, SWT.PUSH);
        cancelButton.setText("取消");
        cancelButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                result = null;
                close();
            }
        });
        
        return composite;
    }
    
    public String getResult() {
        return result;
    }
}

费曼解释:自定义对话框的结构

自定义对话框 = 自定义房子
- 你自己设计
- 自己建造
- 满足你的需求

类比:
自定义对话框 = 定制西装
- 你量尺寸
- 选布料
- 设计款式
- 完全符合你的要求

模态对话框的创建

// 创建模态对话框
CustomDialog dialog = new CustomDialog(shell);
int result = dialog.open();
if (result == Window.OK) {
    String input = dialog.getResult();
    System.out.println("用户输入:" + input);
} else {
    System.out.println("用户取消了对话框");
}

费曼解释:模态对话框的工作原理

模态对话框 = 询问
- 你问一个问题
- 等待对方回答
- 得到答案后,继续

类比:
模态对话框 = 问路
- 你问路人:"去火车站怎么走?"
- 等待路人回答
- 路人回答后,你去火车站

返回值的传递

public class LoginDialog extends Dialog {
    private String username;
    private String password;
    
    public LoginDialog(Shell parentShell) {
        super(parentShell);
    }
    
    @Override
    protected Control createDialogArea(Composite parent) {
        Composite composite = (Composite) super.createDialogArea(parent);
        composite.setLayout(new GridLayout(2, false));
        
        new Label(composite, SWT.NONE).setText("用户名:");
        
        Text usernameText = new Text(composite, SWT.BORDER);
        usernameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        
        new Label(composite, SWT.NONE).setText("密码:");
        
        Text passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD);
        passwordText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        
        Button loginButton = new Button(composite, SWT.PUSH);
        loginButton.setText("登录");
        loginButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                username = usernameText.getText();
                password = passwordText.getText();
                close();
            }
        });
        
        Button cancelButton = new Button(composite, SWT.PUSH);
        cancelButton.setText("取消");
        cancelButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                username = null;
                password = null;
                close();
            }
        });
        
        return composite;
    }
    
    public String getUsername() {
        return username;
    }
    
    public String getPassword() {
        return password;
    }
}

费曼解释:返回值的传递

返回值传递 = 快递
- 寄件人(对话框)
- 收件人(主程序)
- 包裹(返回值)

类比:
返回值传递 = 餐厅点菜
- 你点菜(打开对话框)
- 服务员记下(输入内容)
- 厨师做菜(处理逻辑)
- 菜端上来(返回值)
- 你吃菜(使用返回值)

代码示例:自定义对话框

public class CustomDialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("自定义对话框示例");
        shell.setLayout(new GridLayout(1, false));
        
        // 文本框:显示结果
        Text resultText = 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 = 200;
        resultText.setLayoutData(textData);
        
        // 按钮:打开登录对话框
        Button loginButton = new Button(shell, SWT.PUSH);
        loginButton.setText("打开登录对话框");
        loginButton.addListener(SWT.Selection, event -> {
            LoginDialog loginDialog = new LoginDialog(shell);
            int result = loginDialog.open();
            if (result == Window.OK) {
                String username = loginDialog.getUsername();
                String password = loginDialog.getPassword();
                resultText.append("登录成功!\n");
                resultText.append("用户名:" + username + "\n");
                resultText.append("密码:" + password + "\n");
            } else {
                resultText.append("用户取消了登录\n");
            }
        });
        
        // 按钮:打开输入对话框
        Button inputButton = new Button(shell, SWT.PUSH);
        inputButton.setText("打开输入对话框");
        inputButton.addListener(SWT.Selection, event -> {
            InputDialog inputDialog = new InputDialog(shell);
            int result = inputDialog.open();
            if (result == Window.OK) {
                String input = inputDialog.getInput();
                resultText.append("用户输入:" + input + "\n");
            } else {
                resultText.append("用户取消了输入\n");
            }
        });
        
        shell.setBounds(100, 100, 600, 400);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        display.dispose();
    }
    
    // 登录对话框
    static class LoginDialog extends Dialog {
        private String username;
        private String password;
        
        public LoginDialog(Shell parentShell) {
            super(parentShell);
        }
        
        @Override
        protected void configureShell(Shell newShell) {
            super.configureShell(newShell);
            newShell.setText("登录");
        }
        
        @Override
        protected Control createDialogArea(Composite parent) {
            Composite composite = (Composite) super.createDialogArea(parent);
            composite.setLayout(new GridLayout(2, false));
            composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            
            new Label(composite, SWT.NONE).setText("用户名:");
            
            Text usernameText = new Text(composite, SWT.BORDER);
            GridData usernameData = new GridData(SWT.FILL, SWT.CENTER, true, false);
            usernameData.widthHint = 200;
            usernameText.setLayoutData(usernameData);
            
            new Label(composite, SWT.NONE).setText("密码:");
            
            Text passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD);
            GridData passwordData = new GridData(SWT.FILL, SWT.CENTER, true, false);
            passwordText.setLayoutData(passwordData);
            
            // 按钮面板
            Composite buttonPanel = new Composite(composite, SWT.NONE);
            GridData buttonData = new GridData();
            buttonData.horizontalSpan = 2;
            buttonData.horizontalAlignment = GridData.CENTER;
            buttonPanel.setLayoutData(buttonData);
            buttonPanel.setLayout(new RowLayout(SWT.HORIZONTAL));
            
            Button loginButton = new Button(buttonPanel, SWT.PUSH);
            loginButton.setText("登录");
            loginButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    username = usernameText.getText();
                    password = passwordText.getText();
                    if (username.isEmpty() || password.isEmpty()) {
                        MessageBox messageBox = new MessageBox(getShell(), SWT.ICON_WARNING | SWT.OK);
                        messageBox.setText("警告");
                        messageBox.setMessage("用户名和密码不能为空!");
                        messageBox.open();
                    } else {
                        setReturnCode(OK);
                        close();
                    }
                }
            });
            
            Button cancelButton = new Button(buttonPanel, SWT.PUSH);
            cancelButton.setText("取消");
            cancelButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    setReturnCode(CANCEL);
                    close();
                }
            });
            
            return composite;
        }
        
        public String getUsername() {
            return username;
        }
        
        public String getPassword() {
            return password;
        }
    }
    
    // 输入对话框
    static class InputDialog extends Dialog {
        private String input;
        
        public InputDialog(Shell parentShell) {
            super(parentShell);
        }
        
        @Override
        protected void configureShell(Shell newShell) {
            super.configureShell(newShell);
            newShell.setText("输入");
        }
        
        @Override
        protected Control createDialogArea(Composite parent) {
            Composite composite = (Composite) super.createDialogArea(parent);
            composite.setLayout(new GridLayout(1, false));
            
            Label messageLabel = new Label(composite, SWT.NONE);
            messageLabel.setText("请输入内容:");
            
            Text inputText = new Text(composite, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
            GridData inputData = new GridData(SWT.FILL, SWT.FILL, true, true);
            inputData.widthHint = 300;
            inputData.heightHint = 150;
            inputText.setLayoutData(inputData);
            
            Button okButton = new Button(composite, SWT.PUSH);
            okButton.setText("确定");
            okButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    input = inputText.getText();
                    setReturnCode(OK);
                    close();
                }
            });
            
            Button cancelButton = new Button(composite, SWT.PUSH);
            cancelButton.setText("取消");
            cancelButton.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    input = null;
                    setReturnCode(CANCEL);
                    close();
                }
            });
            
            return composite;
        }
        
        public String getInput() {
            return input;
        }
    }
}

8.7 本章小结

对话框总结

对话框用途类比
MessageBox消息提示通知牌
FileDialog文件选择文件夹
DirectoryDialog目录选择房间
ColorDialog颜色选择调色板
FontDialog字体选择试衣间
CustomDialog自定义对话框自定义房子

费曼测试:你能解释清楚吗?

  1. MessageBox 和自定义对话框的区别?

- MessageBox:预制模板,简单快速 - 自定义对话框:自己设计,自由度高

  1. FileDialog 的 open() 和 save() 的区别?

- open:打开已有文件,只读 - save:保存新文件,写入

  1. 如何获取用户的选择?

- open() 方法返回用户的选择(SWT.YES、SWT.NO、SWT.CANCEL 等)

  1. 自定义对话框如何传递返回值?

- 使用成员变量存储返回值,通过 getter 方法获取

下一章预告

现在你已经掌握了对话框与消息框,
可以与用户友好对话,
但还无法绘制自定义图形。

下一章,我们将学习绘图与动画,
用 Canvas 画出任意图形,
让界面更加生动有趣。

练习题:

  1. 创建一个消息框,包含"确定"和"取消"两个按钮,获取用户选择。
  2. 创建一个文件对话框,支持多文件选择,并列出选择的文件。
  3. 创建一个自定义对话框,包含用户名、密码输入框,点击"登录"按钮返回输入值。

(提示:自定义对话框需要继承 Dialog 类,重写 createDialogArea 方法)

← 返回目录