# Swing自定义组件
# 短暂消息提示
# 封装
package demo;
import javax.swing.*;
import java.awt.*;
public class Toast extends JWindow {
private static final int WIDTH = 300;
private static final int HEIGHT = 50;
private static final int DEFAULT_DURATION = 2000; // 2 秒
private Toast(String message, Color bgColor, int durationMillis) {
JPanel panel = new JPanel();
panel.setBackground(bgColor);
panel.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY));
panel.setLayout(new BorderLayout());
JLabel label = new JLabel(message, SwingConstants.CENTER);
label.setForeground(Color.WHITE);
label.setFont(new Font("微软雅黑", Font.PLAIN, 14));
panel.add(label, BorderLayout.CENTER);
setContentPane(panel);
setSize(WIDTH, HEIGHT);
// 设置在屏幕中央(可自定义位置)
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
int x = (screen.width - WIDTH) / 2;
int y = screen.height / 3; // 顶部偏下一点
setLocation(x, y);
// 淡入显示 & 自动关闭
new Thread(() -> {
setVisible(true);
try {
Thread.sleep(durationMillis);
} catch (InterruptedException ignored) {
}
SwingUtilities.invokeLater(this::dispose);
}).start();
}
public static void showToast(String message) {
showToast(message, DEFAULT_DURATION);
}
public static void showToast(String message, int durationMillis) {
showToast(message, new Color(0, 0, 0, 180), durationMillis);
}
public static void showToast(String message, Color bgColor, int durationMillis) {
SwingUtilities.invokeLater(() -> new Toast(message, bgColor, durationMillis));
}
// 快捷方法:成功 / 错误 / 信息 / 警告
public static void showSuccess(String msg) {
showToast(msg, new Color(76, 175, 80), DEFAULT_DURATION);
}
public static void showError(String msg) {
showToast(msg, new Color(244, 67, 54), DEFAULT_DURATION);
}
public static void showInfo(String msg) {
showToast(msg, new Color(33, 150, 243), DEFAULT_DURATION);
}
public static void showWarning(String msg) {
showToast(msg, new Color(255, 152, 0), DEFAULT_DURATION);
}
}
# 使用
package demo;
import javax.swing.*;
import java.awt.*;
public class ToastTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Toast 消息测试");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
JPanel panel = new JPanel(new GridLayout(4, 1, 10, 10));
JButton successBtn = new JButton("显示成功提示");
successBtn.addActionListener(e -> Toast.showSuccess("保存成功!"));
JButton errorBtn = new JButton("显示错误提示");
errorBtn.addActionListener(e -> Toast.showError("保存失败,请重试。"));
JButton infoBtn = new JButton("显示信息提示");
infoBtn.addActionListener(e -> Toast.showInfo("更新已完成。"));
JButton warnBtn = new JButton("显示警告提示");
warnBtn.addActionListener(e -> Toast.showWarning("数据不完整,请检查。"));
panel.add(successBtn);
panel.add(errorBtn);
panel.add(infoBtn);
panel.add(warnBtn);
frame.setContentPane(panel);
frame.setVisible(true);
});
}
}
# 定制按钮
按钮工厂(
ButtonFactory
) 工具类,用于快速生成统一风格的按钮。
# 封装
package demo;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
public class ButtonFactory {
/**
* 通用按钮创建方法
*/
public static JButton createButton(String text, Color fg, Color bg, Font font, Icon icon, Integer mnemonic) {
JButton button = new JButton(text);
button.setForeground(fg);
button.setBackground(bg);
button.setFocusPainted(false);
button.setFont(font != null ? font : button.getFont());
if (icon != null) {
button.setIcon(icon);
}
if (mnemonic != null) {
button.setMnemonic(mnemonic); // 支持快捷键
}
// 可选统一按钮样式,例如圆角、边框、hover 效果可以后续增强
return button;
}
// 主按钮(蓝色)
public static JButton createPrimaryButton(String text) {
return createButton(
text,
Color.WHITE,
new Color(33, 150, 243),
new Font("微软雅黑", Font.PLAIN, 14),
null,
null
);
}
// 次按钮(灰色)
public static JButton createSecondaryButton(String text) {
return createButton(
text,
Color.BLACK,
new Color(224, 224, 224),
new Font("微软雅黑", Font.PLAIN, 14),
null,
null
);
}
// 成功按钮(绿色)
public static JButton createSuccessButton(String text) {
return createButton(
text,
Color.WHITE,
new Color(76, 175, 80),
new Font("微软雅黑", Font.PLAIN, 14),
null,
null
);
}
// 危险按钮(红色)
public static JButton createDangerButton(String text) {
return createButton(
text,
Color.WHITE,
new Color(244, 67, 54),
new Font("微软雅黑", Font.PLAIN, 14),
null,
null
);
}
// 自定义字体大小版本
public static JButton createPrimaryButton(String text, int fontSize) {
return createButton(
text,
Color.WHITE,
new Color(33, 150, 243),
new Font("微软雅黑", Font.PLAIN, fontSize),
null,
null
);
}
// 添加快捷键按钮
public static JButton createButtonWithShortcut(String text, char shortcutKey, Color bgColor) {
return createButton(
text,
Color.WHITE,
bgColor,
new Font("微软雅黑", Font.PLAIN, 14),
null,
KeyEvent.getExtendedKeyCodeForChar(shortcutKey)
);
}
}
# 使用
package demo;
import javax.swing.*;
import java.awt.*;
public class ButtonFactoryTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("按钮工厂测试");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
frame.setLocationRelativeTo(null);
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(5, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
panel.add(ButtonFactory.createPrimaryButton("主按钮"));
panel.add(ButtonFactory.createSecondaryButton("次按钮"));
panel.add(ButtonFactory.createSuccessButton("成功"));
panel.add(ButtonFactory.createDangerButton("危险"));
panel.add(ButtonFactory.createButtonWithShortcut("快捷键按钮 (Alt+S)", 'S', new Color(255, 152, 0)));
frame.setContentPane(panel);
frame.setVisible(true);
});
}
}
# 动态控制Tab
用
JTabbedPane
做核心,封装一个DynamicTabPanel
类,然后在主JFrame
初始化时动态传入 tab 的数量、标题和内容。
# 封装
package com.bbs;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class DynamicTabPanel extends JPanel {
private JTabbedPane tabbedPane;
public DynamicTabPanel() {
setLayout(new BorderLayout());
tabbedPane = new JTabbedPane();
add(tabbedPane, BorderLayout.CENTER);
}
/**
* 添加一个新 Tab
* @param title 标签标题
* @param content 标签内容(任何 JComponent)
*/
public void addTab(String title, JComponent content) {
tabbedPane.addTab(title, content);
}
/**
* 批量添加多个 Tab
* @param tabs List of TabItem(title + component)
*/
public void setTabs(List<TabItem> tabs) {
tabbedPane.removeAll(); // 清空旧的
for (TabItem tab : tabs) {
addTab(tab.getTitle(), tab.getContent());
}
}
public JTabbedPane getTabbedPane() {
return tabbedPane;
}
// 静态内部类表示单个 Tab
public static class TabItem {
private String title;
private JComponent content;
public TabItem(String title, JComponent content) {
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public JComponent getContent() {
return content;
}
}
}
# 使用
package com.bbs;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class DynamicTabTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("动态 Tab 面板示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
DynamicTabPanel tabPanel = new DynamicTabPanel();
// 动态生成 tab 数据
List<DynamicTabPanel.TabItem> tabs = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
JPanel content = new JPanel();
content.add(new JLabel("这是第 " + i + " 个标签页的内容"));
tabs.add(new DynamicTabPanel.TabItem("标签页 " + i, content));
}
tabPanel.setTabs(tabs);
frame.add(tabPanel, BorderLayout.CENTER);
frame.setVisible(true);
});
}
}
# 面板图片操作
# 图片上传
功能 | 实现情况 |
---|---|
支持本地文件选择 | ✅ |
自动复制到 /upload | ✅ |
自动创建 upload 目录 | ✅ |
命名统一、避免重复 | ✅(时间戳命名) |
显示上传后图片 | ✅ |
输出最终图片路径(可存库) | ✅ |
package com.bbs.view;
import com.bbs.dao.BookDao;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.file.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ImageUpload extends JFrame {
private JLabel imageLabel;
private JButton selectButton, uploadButton, resetButton,backButton;
private File selectedImageFile;
private String uploadedImagePath;
public ImageUpload(Integer id) throws Exception {
super("图片上传");
this.bookId = id;
selectButton = new JButton("选择图片");
uploadButton = new JButton("上传图片");
resetButton = new JButton("重置图片");
JButton backButton = new JButton("返回"); // ✅ 新增返回按钮
imageLabel = new JLabel("暂无图片", SwingConstants.CENTER);
imageLabel.setPreferredSize(new Dimension(200, 200));
imageLabel.setBorder(BorderFactory.createLineBorder(Color.GRAY));
imageLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
imageLabel.setForeground(Color.GRAY);
// 整体使用 BorderLayout
setLayout(new BorderLayout());
// 中部内容:按钮 + 图片
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS));
// 原按钮行
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 15, 10));
buttonPanel.add(selectButton);
buttonPanel.add(uploadButton);
buttonPanel.add(resetButton);
// 图片显示区域
JPanel imagePanel = new JPanel();
imagePanel.add(imageLabel);
centerPanel.add(buttonPanel);
centerPanel.add(imagePanel);
add(centerPanel, BorderLayout.CENTER);
// 底部返回按钮单独一行
JPanel bottomPanel = new JPanel();
bottomPanel.add(backButton);
add(bottomPanel, BorderLayout.SOUTH);
// 事件绑定
selectButton.addActionListener(e -> chooseImageFile());
uploadButton.addActionListener(e -> confirmUpload());
resetButton.addActionListener(e -> resetImage());
backButton.addActionListener(e -> dispose()); // 关闭窗口
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(450, 360);
setLocationRelativeTo(null);
setVisible(true);
}
private void chooseImageFile() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
final String[] extensions = { "jpg", "jpeg", "png", "gif", "bmp" };
@Override
public boolean accept(File f) {
if (f.isDirectory()) return true;
String name = f.getName().toLowerCase();
for (String ext : extensions) {
if (name.endsWith(ext)) return true;
}
return false;
}
@Override
public String getDescription() {
return "图片文件 (*.jpg, *.png, *.gif, *.bmp)";
}
});
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
selectedImageFile = fileChooser.getSelectedFile();
previewImage(selectedImageFile);
}
}
private void resetImage() {
selectedImageFile = null;
uploadedImagePath = null;
imageLabel.setIcon(null);
imageLabel.setText("暂无图片");
imageLabel.setForeground(Color.GRAY);
}
private void previewImage(File file) {
if (file == null || !file.exists()) return;
ImageIcon icon = new ImageIcon(file.getAbsolutePath());
Image img = icon.getImage().getScaledInstance(
imageLabel.getWidth(), imageLabel.getHeight(), Image.SCALE_SMOOTH);
imageLabel.setIcon(new ImageIcon(img));
imageLabel.setText(null);
}
private void confirmUpload() {
if (selectedImageFile == null) {
JOptionPane.showMessageDialog(this, "请先选择图片!");
return;
}
String path = saveToUploadDirectory(selectedImageFile);
if (path != null) {
uploadedImagePath = path;
JOptionPane.showMessageDialog(this, "图片上传成功!\n路径:" + uploadedImagePath);
System.out.println("图片已上传至:" + uploadedImagePath);
// TODO: 保存路径
dispose();
} else {
JOptionPane.showMessageDialog(this, "图片上传失败!");
}
}
private String saveToUploadDirectory(File sourceFile) {
try {
File uploadDir = new File("upload");
if (!uploadDir.exists()) uploadDir.mkdirs();
String originalName = sourceFile.getName();
String ext = originalName.substring(originalName.lastIndexOf("."));
String timeStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String newFileName = "dish_" + timeStr + ext;
File destFile = new File(uploadDir, newFileName);
Files.copy(sourceFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
return destFile.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
# 表格图片展示
# 封装
- 图片自适应
- 表格可指定显示图片的列,可设置表格字体、表头字体大小
- 表格可指定隐藏某些行
- 方便复用
package com.bbs.util;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.io.File;
import java.net.URL;
import java.util.Set;
public class PlusTable extends JTable {
public PlusTable(Object[][] data, String[] columnNames, Set<Integer> imageColumns, Set<Integer> hiddenColumns) {
super(new DefaultTableModel(data, columnNames) {
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
});
// getTableHeader().setFont(new Font("宋体", Font.BOLD, 18)); // 设置表头字体
// setFont(new Font("宋体", Font.PLAIN, 18)); // 设置表格字体
setRowHeight(100); // 设置行高
// 图片列渲染器
for (int col : imageColumns) {
getColumnModel().getColumn(col).setCellRenderer(new ProportionalImageRenderer());
}
// 非图片列居中
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
for (int i = 0; i < getColumnCount(); i++) {
if (!imageColumns.contains(i)) {
getColumnModel().getColumn(i).setCellRenderer(centerRenderer);
}
}
// 隐藏指定列
if (hiddenColumns != null) {
for (int i = getColumnCount() - 1; i >= 0; i--) { // 从后向前删,避免越界
if (hiddenColumns.contains(i)) {
getColumnModel().removeColumn(getColumnModel().getColumn(i));
}
}
}
}
// 图片渲染器内部类
static class ProportionalImageRenderer extends JLabel implements TableCellRenderer {
private static final int VERTICAL_PADDING = 5;
public ProportionalImageRenderer() {
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(VERTICAL_PADDING, 0, VERTICAL_PADDING, 0));
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus,
int row, int column) {
setText(null);
setIcon(null);
if (value instanceof String) {
String path = (String) value;
try {
ImageIcon icon;
if (path.startsWith("http://") || path.startsWith("https://")) {
icon = new ImageIcon(new URL(path));
} else {
File imgFile = new File(path);
if (!imgFile.exists()) {
setText("图片不存在");
return this;
}
icon = new ImageIcon(path);
}
int cellWidth = table.getColumnModel().getColumn(column).getWidth();
int cellHeight = table.getRowHeight(row) - 2 * VERTICAL_PADDING;
int imgWidth = icon.getIconWidth();
int imgHeight = icon.getIconHeight();
if (imgWidth > 0 && imgHeight > 0) {
double widthRatio = (double) cellWidth / imgWidth;
double heightRatio = (double) cellHeight / imgHeight;
double scale = Math.min(widthRatio, heightRatio);
int newWidth = (int) (imgWidth * scale);
int newHeight = (int) (imgHeight * scale);
Image scaledImg = icon.getImage().getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH);
setIcon(new ImageIcon(scaledImg));
}
} catch (Exception ex) {
setText("加载失败");
}
}
return this;
}
}
}
# 如何使用
private JTable table;
table = new PlusTable(new Object[][]{}, columnNames, Set.of(5),Set.of());
table.setRowHeight(100);
scrollPane.setViewportView(table);
initTable();
private void fillTable(List<Book> bookList) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.setRowCount(0); // 清除旧数据
for (Book book : bookList) {
model.addRow(new Object[]{
book.getId(),
book.getTitle(),
book.getAuthor(),
book.getIsbn(),
book.getCount(),
book.getCover()
});
}
}
# Label展示图片
# 封装
- 图片自适应完整呈现
package com.bbs;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import javax.imageio.ImageIO;
public class AutoResizeImageLabel extends JLabel {
private Image originalImage;
public AutoResizeImageLabel() {
this.setHorizontalAlignment(CENTER);
this.setVerticalAlignment(CENTER);
}
/**
* 直接从 Image 对象设置图像
*/
public void setImage(Image image) {
this.originalImage = image;
repaint();
}
/**
* 从文件路径加载图像并设置
*/
public boolean setImageFromFile(String path) {
try {
File file = new File(path);
if (!file.exists()) {
System.err.println("文件不存在: " + path);
return false;
}
Image img = ImageIO.read(file);
if (img != null) {
setImage(img);
return true;
} else {
System.err.println("无法读取图片文件: " + path);
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (originalImage != null) {
int labelWidth = getWidth();
int labelHeight = getHeight();
int imgWidth = originalImage.getWidth(null);
int imgHeight = originalImage.getHeight(null);
if (imgWidth > 0 && imgHeight > 0) {
double scale = Math.min((double) labelWidth / imgWidth, (double) labelHeight / imgHeight);
int drawWidth = (int) (imgWidth * scale);
int drawHeight = (int) (imgHeight * scale);
int x = (labelWidth - drawWidth) / 2;
int y = (labelHeight - drawHeight) / 2;
g.drawImage(originalImage, x, y, drawWidth, drawHeight, this);
}
}
}
}
# 如何使用
package com.bbs;
import javax.swing.*;
public class TestAutoResizeLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("自动适应图片展示");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.getContentPane().setLayout(null);
AutoResizeImageLabel imageLabel = new AutoResizeImageLabel();
imageLabel.setBounds(60, 32, 436, 213);
frame.getContentPane().add(imageLabel);
// 使用封装的方法加载图片
String path = "upload/pngsucai_1359311_90049c.png";
boolean loaded = imageLabel.setImageFromFile(path);
JLabel lblNewLabel = new JLabel("内容测试");
lblNewLabel.setBounds(257, 294, 54, 15);
frame.getContentPane().add(lblNewLabel);
if (!loaded) {
JOptionPane.showMessageDialog(frame, "图片加载失败: " + path, "错误", JOptionPane.ERROR_MESSAGE);
}
frame.setVisible(true);
});
}
}
# 下拉框组件
# 目标
封装一个可复用的下拉框组件 CustomComboBox<T>
,支持以下功能:
- 快速初始化数据源
- 设置默认选项
- 获取当前选中项
- 支持泛型(如:
String
、Integer
、自定义对象) - 提供常用方法如刷新数据、设置提示文本等
# 组件代码:CustomComboBox.java
import javax.swing.*;
import java.awt.*;
import java.util.List;
import java.util.Vector;
public class CustomComboBox<T> extends JPanel {
private JComboBox<T> comboBox;
private DefaultComboBoxModel<T> model;
public CustomComboBox(List<T> items) {
this(items, null);
}
public CustomComboBox(List<T> items, T selectedItem) {
setLayout(new BorderLayout());
model = new DefaultComboBoxModel<>(new Vector<>(items));
comboBox = new JComboBox<>(model);
if (selectedItem != null) {
comboBox.setSelectedItem(selectedItem);
}
add(comboBox, BorderLayout.CENTER);
}
public void setSelectedItem(T item) {
comboBox.setSelectedItem(item);
}
public T getSelectedItem() {
return (T) comboBox.getSelectedItem();
}
public void refreshItems(List<T> newItems) {
model.removeAllElements();
for (T item : newItems) {
model.addElement(item);
}
}
public JComboBox<T> getComboBox() {
return comboBox;
}
public void setPrompt(String prompt) {
comboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value == null && index == -1) {
setText(prompt);
}
return this;
}
});
}
}
# 示例用法
import javax.swing.*;
import java.util.Arrays;
public class TestComboBox {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("下拉框组件示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 150);
CustomComboBox<String> comboBox = new CustomComboBox<>(
Arrays.asList("苹果", "香蕉", "橘子", "西瓜"), "香蕉"
);
comboBox.setPrompt("请选择水果");
frame.getContentPane().add(comboBox, "North");
JButton btn = new JButton("获取选中值");
btn.addActionListener(e -> {
String selected = comboBox.getSelectedItem();
JOptionPane.showMessageDialog(frame, "你选择的是: " + selected);
});
frame.getContentPane().add(btn, "South");
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
← 表格行高列宽调整