跳到主要内容IDEA 插件开发:Swing 图形界面设计指南 | 极客日志Javajava
IDEA 插件开发:Swing 图形界面设计指南
Swing 是 Java 桌面应用开发的核心 GUI 框架,相比 AWT 具有跨平台一致性优势。本文系统介绍了 IDEA 插件开发中 Swing 的基础架构、常用容器(JFrame、JDialog)、布局管理器(BorderLayout、FlowLayout 等)、事件处理机制以及进度条、列表、表格、树形控件的高级用法。通过提供完整的代码示例和注意事项,帮助开发者快速掌握 Swing 图形界面设计与定制技巧,提升插件用户体验。
IDEA 插件开发:Swing 图形界面设计指南
Java GUI 提供了 AWT 和 Swing 两套设计体系。Swing 基于 AWT 构建,功能上基本一致,但显示样式有显著差异:AWT 会匹配当前操作系统的 UI 风格,而 Swing 则保持统一。因此,在 IDEA 插件开发中,建议优先采用 Swing-GUI。
一、Idea-UI 开发基础
1. UI 控件架构
IDEA 的图形界面底层基于 Swing 开发,官方文档也有明确推荐。虽然 Swing 组件繁多,但核心逻辑相对简单。所有 GUI 开发基本遵循以下层级关系:

2. Swing 体系
开发时只需掌握这张核心架构图,后续遇到具体控件再查阅 API 文档即可。无需过度深究底层原理(Canvas 开发除外,那涉及较多图形学知识)。

二、Swing 容器
本章将从容器、布局、复杂控件几个维度给出示例。
1. JFrame
创建一个空白窗口是入门第一步。
import javax.swing.*;
public class WindowsDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("窗口演示");
frame.setLocation(100, 100);
frame.setSize(500, 300);
frame.setVisible(true);
}
}

2. JDialog
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DialogTest {
public static void main(String[] args) {
JFrame frame = new JFrame("测试 Dialog");
JDialog d1 = new JDialog(frame, "模式对话框", true);
d1.setBounds(20, 30, 300, 200);
JButton b1 = new JButton("打开模式对话框");
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
d1.setVisible(true);
}
});
frame.add(b1, BorderLayout.NORTH);
frame.pack();
frame.setVisible(true);
}
}
3. MessageDialog
这是简化版的弹出确认框,注意这些 Dialog 默认都是模态化的。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MessageDialogTest {
JFrame jf = new JFrame("测试消息对话框");
JTextArea jta = new JTextArea(6, 30);
JButton btn = new JButton(new AbstractAction("弹出消息对话框") {
@Override
public void actionPerformed(ActionEvent e) {
String text = jta.getText();
JOptionPane.showMessageDialog(jf, text, "消息对话框", JOptionPane.WARNING_MESSAGE,
new ImageIcon("E:\\GUISwing\\img\\component\\female.png"));
}
});
public void init() {
jf.add(jta);
jf.add(btn, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new MessageDialogTest().init();
}
}
4. 为容器添加工具栏
Frame 菜单
菜单分为嵌入 Frame 中的主菜单和右键上下文菜单两种。
import javax.swing.*;
import java.awt.*;
public class SimpleMenu {
private JFrame frame = new JFrame("测试菜单相关组件");
private JMenuBar menuBar = new JMenuBar();
private JMenu fileMenu = new JMenu("文件");
private JMenu editMenu = new JMenu("编辑");
private JMenuItem copy = new JMenuItem("复制");
private JMenuItem paste = new JMenuItem("粘贴");
public void init() {
editMenu.add(copy);
editMenu.add(paste);
menuBar.add(fileMenu);
menuBar.add(editMenu);
frame.setJMenuBar(menuBar);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new SimpleMenu().init();
}
}
Toolbar 工具栏
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class JToolBarTest {
JFrame jf = new JFrame("测试工具条");
JTextArea jta = new JTextArea(6, 35);
JToolBar jToolBar = new JToolBar("播放工具条", SwingConstants.HORIZONTAL);
Action pre = new AbstractAction("上一曲", new ImageIcon("E:\\GUISwing\\img\\component\\上一曲 16.png")) {
@Override
public void actionPerformed(ActionEvent e) {
jta.append("上一曲.\n");
}
};
public void init() {
JButton preBtn = new JButton(pre);
jToolBar.add(preBtn);
jToolBar.addSeparator();
jToolBar.setFloatable(true);
jf.add(jToolBar, BorderLayout.NORTH);
JScrollPane jScrollPane = new JScrollPane(jta);
jf.add(jScrollPane);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new JToolBarTest().init();
}
}
5. 特殊容器
JSplitPane - 可调整大小的面板
import javax.swing.*;
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@Data
@AllArgsConstructor
class Book {
private String name;
private String desc;
}
public class SplitPaneTest {
Book[] books = {
new Book("java 自学宝典", "国内关于 Java 编程最全面的图书,看得懂,学得会"),
new Book("轻量级的 JAVAEE 企业应用实战", "SSM 整合开发的经典图书,值得拥有")
};
JFrame jf = new JFrame("测试 JSplitPane");
JList<Book> bookJList = new JList<>(books);
JLabel bookCover = new JLabel();
JTextArea bookDesc = new JTextArea();
public void init() {
bookJList.setPreferredSize(new Dimension(150, 400));
bookCover.setPreferredSize(new Dimension(220, 270));
bookDesc.setPreferredSize(new Dimension(220, 130));
bookJList.addListSelectionListener(e -> {
Book book = bookJList.getSelectedValue();
if (book != null) {
bookDesc.setText(book.getDesc());
}
});
JSplitPane left = new JSplitPane(JSplitPane.VERTICAL_SPLIT, bookCover, new JScrollPane(bookDesc));
left.setOneTouchExpandable(true);
JSplitPane hole = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, bookJList);
hole.setContinuousLayout(true);
hole.setDividerSize(10);
jf.add(hole);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new SplitPaneTest().init();
}
}

TabbedPane - 标签页面板
import javax.swing.*;
import java.awt.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class JTabbedPaneTest {
JFrame jf = new JFrame("测试 JTabbedPane");
JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT);
public void init() {
tabbedPane.addTab("用户管理", new ImageIcon("swing\\img\\container\\open.gif"),
new JList<String>(new String[]{"用户一", "用户二", "用户三"}));
tabbedPane.addTab("商品管理", new JList<String>(new String[]{"商品一", "商品二", "商品三"}));
tabbedPane.addTab("订单管理", new ImageIcon("swing\\img\\container\\open.gif"),
new JList<String>(new String[]{"订单一", "订单二", "订单三", "订单四"}));
tabbedPane.setEnabledAt(0, false);
tabbedPane.setSelectedIndex(1);
tabbedPane.addChangeListener(e -> {
int selectedIndex = tabbedPane.getSelectedIndex();
JOptionPane.showMessageDialog(jf, "当前选中了第 " + (selectedIndex + 1) + "个标签");
});
jf.add(tabbedPane);
jf.setBounds(400, 400, 400, 400);
jf.setResizable(false);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new JTabbedPaneTest().init();
}
}

三、Swing 布局
1. BorderLayout
边界布局将容器分为北、南、东、西、中五个区域。未设置的区域会自动留空,适合实现行列布局模式。
import javax.swing.*;
import java.awt.*;
public class BorderLayoutTest extends JFrame {
public BorderLayoutTest() {
JButton jButton = new JButton("中间");
JButton jButton2 = new JButton("北间");
JButton jButton3 = new JButton("西间");
JButton jButton4 = new JButton("东间");
JButton jButton5 = new JButton("南间");
this.add(jButton, BorderLayout.CENTER);
this.add(jButton2, BorderLayout.NORTH);
this.add(jButton3, BorderLayout.WEST);
this.add(jButton4, BorderLayout.EAST);
this.add(jButton5, BorderLayout.SOUTH);
this.setTitle("演示边界布局管理器");
this.setSize(300, 200);
this.setLocation(200, 200);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new BorderLayoutTest();
}
}
2. FlowLayout
流式布局按添加顺序依次摆放,超出容器宽度自动换行。
import javax.swing.*;
import java.awt.*;
public class FlowLayoutTest extends JFrame {
public FlowLayoutTest() {
JButton jButton1 = new JButton("A");
JButton jButton2 = new JButton("B");
JButton jButton3 = new JButton("C");
JButton jButton4 = new JButton("D");
JButton jButton5 = new JButton("E");
this.add(jButton1);
this.add(jButton2);
this.add(jButton3);
this.add(jButton4);
this.add(jButton5);
this.setLayout(new FlowLayout(FlowLayout.LEFT));
this.setTitle("演示流布局管理器");
this.setSize(300, 200);
this.setLocation(200, 200);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
}
public static void main(String[] args) {
new FlowLayoutTest();
}
}
3. GridLayout
import javax.swing.*;
import java.awt.*;
public class GridLayoutTest extends JFrame {
int size = 9;
JButton[] jButton = new JButton[size];
public GridLayoutTest() {
for (int i = 0; i < size; i++) {
jButton[i] = new JButton(String.valueOf(i + 1));
this.add(jButton[i]);
}
this.setLayout(new GridLayout(3, 3, 10, 30));
this.setTitle("演示网格布局管理器");
this.setSize(400, 400);
this.setLocation(200, 200);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
}
public static void main(String[] args) {
new GridLayoutTest();
}
}
4. BoxLayout
import javax.swing.*;
import java.awt.*;
public class BoxLayoutTest {
public static void main(String[] args) {
Frame frame = new Frame("这里测试 BoxLayout");
BoxLayout boxLayout = new BoxLayout(frame, BoxLayout.X_AXIS);
frame.setLayout(boxLayout);
frame.add(new Button("按钮 1"));
frame.add(new Button("按钮 2"));
frame.pack();
frame.setVisible(true);
}
}
四、Swing 事件
事件源可以绑定多个监听器,也允许多个同类型事件源共享一个监听器。内部会形成事件链依次执行。
1. 定制侦听器
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class EventDemo1 {
Frame frame = new Frame("这里测试事件处理");
TextField tf = new TextField(30);
Button ok = new Button("确定");
public void init() {
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("ok 按钮被点击了...");
tf.setText("Hello World");
}
});
frame.add(tf, BorderLayout.NORTH);
frame.add(ok);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new EventDemo1().init();
}
}
2. 共享侦听器
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("ok 按钮被点击了...");
}
}
public class EventDemoShared {
Frame frame = new Frame("这里测试事件处理");
TextField tf = new TextField(30);
Button ok = new Button("确定");
public void init() {
MyListener myListener = new MyListener();
ok.addActionListener(myListener);
frame.add(tf, BorderLayout.NORTH);
frame.add(ok);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new EventDemoShared().init();
}
}
五、Swing 进度条
主要使用 JProgressBar、ProgressMonitor、BoundedRangeModel 实现。
1. 主线程内的进度条
注意: 耗时操作放在主线程会导致界面卡死,实际开发中建议放入子线程。
import javax.swing.*;
import java.awt.*;
public class JProgressTest {
JFrame jf = new JFrame("测试进度条");
JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
public void init() {
Box vBox = Box.createVerticalBox();
bar.setStringPainted(true);
bar.setBorderPainted(true);
jf.setLayout(new FlowLayout());
jf.add(vBox);
jf.add(bar);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
for (int i = 0; i <= 100; i++) {
bar.setValue(i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new JProgressTest().init();
}
}
2. 线程内的进度条(推荐)
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class JProgressTest2 {
JFrame jf = new JFrame("测试进度条");
JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
public void init() {
Box vBox = Box.createVerticalBox();
bar.setStringPainted(true);
bar.setBorderPainted(true);
jf.setLayout(new FlowLayout());
jf.add(vBox);
jf.add(bar);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
SimulaterActivity simulaterActivity = new SimulaterActivity();
simulaterActivity.setAmount(bar.getMaximum());
new Thread(simulaterActivity).start();
Timer timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int current = simulaterActivity.getCurrent();
bar.setValue(current);
}
});
timer.start();
bar.addChangeListener(e -> {
int value = bar.getValue();
if (value == simulaterActivity.getAmount()) {
timer.stop();
}
});
}
@Data
private class SimulaterActivity implements Runnable {
private int amount;
private volatile int current;
@Override
public void run() {
while (current < amount) {
try {
Thread.currentThread().sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
current++;
}
}
}
public static void main(String[] args) {
new JProgressTest2().init();
}
}

3. 获取进度条数据
Swing 组件将外观与数据分离,JProgressBar 内置 BoundedRangeModel 保存状态。通过 Model 可以更灵活地控制数据。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class JProgressTest3 {
JFrame jf = new JFrame("测试进度条");
JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
BoundedRangeModel model = bar.getModel();
public void init() {
Box vBox = Box.createVerticalBox();
bar.setStringPainted(true);
bar.setBorderPainted(true);
jf.setLayout(new FlowLayout());
jf.add(vBox);
jf.add(bar);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
SimulaterActivity simulaterActivity = new SimulaterActivity();
simulaterActivity.setAmount(bar.getMaximum());
new Thread(simulaterActivity).start();
Timer timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int current = simulaterActivity.getCurrent();
model.setValue(current);
}
});
timer.start();
model.addChangeListener(e -> {
int value = model.getValue();
if (value == simulaterActivity.getAmount()) {
timer.stop();
}
});
}
@Data
private class SimulaterActivity implements Runnable {
private int amount;
private volatile int current;
@Override
public void run() {
while (current < amount) {
try {
Thread.currentThread().sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
current++;
}
}
}
public static void main(String[] args) {
new JProgressTest3().init();
}
}
4. ProgressMonitor
ProgressMonitor 创建的对话框包含固定样式的进度条,不可自定义边框或方向,但使用简单。
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ProgressMonitorTest {
Timer timer;
public void init() {
ProgressMonitor monitor = new ProgressMonitor(null, "等待任务完成", "已完成", 0, 100);
SimulaterActivity simulaterActivity = new SimulaterActivity(100);
new Thread(simulaterActivity).start();
timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int current = simulaterActivity.getCurrent();
monitor.setProgress(current);
if (monitor.isCanceled()) {
timer.stop();
monitor.close();
System.exit(0);
}
}
});
timer.start();
}
@Data
private class SimulaterActivity implements Runnable {
private volatile int current = 0;
private int amount;
public SimulaterActivity(int amount) {
this.amount = amount;
}
@Override
public void run() {
while (current < amount) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
current++;
}
}
}
public static void main(String[] args) {
new ProgressMonitorTest().init();
}
}
六、Swing 下拉列表
1. 简单列表
展示基本的 JList 和 JComboBox 用法。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
public class ListTest {
JFrame jf = new JFrame("列表框测试");
String[] books = {"java 自学宝典", "轻量级 javaEE 企业应用实战", "Android 基础教程", "jQuery 实战教程", "SpringBoot 企业级开发"};
JPanel layoutPanel = new JPanel();
ButtonGroup layoutGroup = new ButtonGroup();
JPanel selectModePanel = new JPanel();
ButtonGroup selectModeGroup = new ButtonGroup();
JList<String> bookList;
JComboBox<String> bookSelector;
public void init() {
bookList = new JList<>(books);
addBtn2LayoutPanel("纵向滚动", JList.VERTICAL);
addBtn2LayoutPanel("纵向换行", JList.VERTICAL_WRAP);
addBtn2LayoutPanel("横向换行", JList.HORIZONTAL_WRAP);
addBtn2SelectModelPanel("无限制", ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
addBtn2SelectModelPanel("单选", ListSelectionModel.SINGLE_SELECTION);
addBtn2SelectModelPanel("单范围", ListSelectionModel.SINGLE_INTERVAL_SELECTION);
bookList.setVisibleRowCount(3);
bookList.setSelectionInterval(2, 4);
bookList.addListSelectionListener(e -> {
List<String> selectedValuesList = bookList.getSelectedValuesList();
});
Box bookListVBox = Box.createVerticalBox();
bookListVBox.add(new JScrollPane(bookList));
bookListVBox.add(layoutPanel);
bookListVBox.add(selectModePanel);
Vector<String> vector = new Vector<>();
for (String book : books) vector.add(book);
bookSelector = new JComboBox<>(vector);
bookSelector.setEditable(true);
bookSelector.setMaximumRowCount(4);
Box topBox = Box.createHorizontalBox();
topBox.add(bookListVBox);
JPanel bookSelectPanel = new JPanel();
bookSelectPanel.add(bookSelector);
topBox.add(bookSelectPanel);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BorderLayout());
Box holeBox = Box.createVerticalBox();
holeBox.add(topBox);
holeBox.add(bottomPanel);
jf.add(holeBox);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public void addBtn2LayoutPanel(String name, int layoutType) {
layoutPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "确定选项布局"));
JRadioButton button = new JRadioButton(name);
layoutPanel.add(button);
if (layoutGroup.getButtonCount() == 0) button.setSelected(true);
layoutGroup.add(button);
button.addActionListener(e -> bookList.setLayoutOrientation(layoutType));
}
public void addBtn2SelectModelPanel(String name, int selectionModel) {
selectModePanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "确定选择模式"));
JRadioButton button = new JRadioButton(name);
selectModePanel.add(button);
if (selectModeGroup.getButtonCount() == 0) button.setSelected(true);
selectModeGroup.add(button);
button.addActionListener(e -> bookList.setSelectionMode(selectionModel));
}
public static void main(String[] args) {
new ListTest().init();
}
}

2. 固定值的列表
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Vector;
public class NumberListModel extends AbstractListModel<BigDecimal> {
BigDecimal start, end, step;
public NumberListModel(BigDecimal start, BigDecimal end, BigDecimal step) {
this.start = start;
this.end = end;
this.step = step;
}
@Override
public int getSize() {
int floor = (int) Math.floor(end.subtract(start).divide(step, 2, RoundingMode.HALF_DOWN).doubleValue());
return floor + 1;
}
@Override
public BigDecimal getElementAt(int index) {
BigDecimal ele = new BigDecimal(index).multiply(step).add(start).setScale(2, RoundingMode.HALF_DOWN);
return ele;
}
}
public class NumberComboBoxListModel extends NumberListModel implements ComboBoxModel<BigDecimal> {
private int selectedId;
public NumberComboBoxListModel(BigDecimal start, BigDecimal end, BigDecimal step) {
super(start, end, step);
}
@Override
public void setSelectedItem(Object anItem) {
if (anItem instanceof BigDecimal) {
BigDecimal curr = (BigDecimal) anItem;
selectedId = curr.subtract(super.start).divide(super.step, 2, RoundingMode.HALF_DOWN).intValue();
}
}
@Override
public Object getSelectedItem() {
BigDecimal item = new BigDecimal(selectedId).multiply(super.step).add(super.start).setScale(1, RoundingMode.HALF_DOWN);
return item;
}
}
配合 ListModelTest 使用,展示动态生成的列表项。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.math.BigDecimal;
import java.util.List;
public class ListModelTest {
JFrame jf = new JFrame("测试 ListModel");
JList<BigDecimal> jList = new JList<>(new NumberListModel(new BigDecimal(1), new BigDecimal(21), new BigDecimal(2)));
JComboBox<BigDecimal> jComboBox = new JComboBox<>(new NumberComboBoxListModel(new BigDecimal(0.1), new BigDecimal(1.2), new BigDecimal(0.1)));
JLabel label = new JLabel("您选择的值是:");
JTextField jTextField = new JTextField(15);
public void init() {
jList.setVisibleRowCount(4);
jList.setSelectionInterval(2, 4);
jList.setFixedCellWidth(90);
jList.setFixedCellHeight(30);
jList.addListSelectionListener(e -> {
List<BigDecimal> selectedValuesList = jList.getSelectedValuesList();
jTextField.setText("");
for (BigDecimal item : selectedValuesList) {
jTextField.setText(jTextField.getText() + item.toString() + ",");
}
});
jComboBox.setMaximumRowCount(4);
jComboBox.addItemListener(e -> {
Object selectedItem = jComboBox.getSelectedItem();
jTextField.setText(selectedItem.toString());
});
Box hBox = Box.createHorizontalBox();
hBox.add(new JScrollPane(jList));
JPanel tempPanel = new JPanel();
tempPanel.add(jComboBox);
hBox.add(tempPanel);
jf.add(hBox);
JPanel bottomPanel = new JPanel();
bottomPanel.add(label);
bottomPanel.add(jTextField);
jf.add(bottomPanel, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new ListModelTest().init();
}
}

3. 动态改变列表的值
使用 DefaultListModel 实现列表数据的增删改。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class DefaultListModelTest {
JFrame jf = new JFrame("测试 DefaultListModel");
JTextField bookName = new JTextField(20);
JButton removeBtn = new JButton("删除选中图书");
JButton addBtn = new JButton("添加指定图书");
JList<String> bookList;
DefaultListModel<String> model = new DefaultListModel<>();
public void init() {
model.addElement("java 自学宝典");
model.addElement("轻量级 JavaEE 企业应用实战");
model.addElement("Android 基础教程");
model.addElement("JQuery 实战教程");
bookList = new JList<>(model);
bookList.setVisibleRowCount(4);
bookList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
jf.add(new JScrollPane(bookList));
addBtn.addActionListener(e -> {
String name = bookName.getText();
if (!name.trim().equals("")) {
model.addElement(name);
}
});
removeBtn.addActionListener(e -> {
int selectedIndex = bookList.getSelectedIndex();
if (selectedIndex >= 0) {
model.remove(selectedIndex);
}
});
JPanel bottomPanel = new JPanel();
bottomPanel.add(bookName);
bottomPanel.add(addBtn);
bottomPanel.add(removeBtn);
jf.add(bottomPanel, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new DefaultListModelTest().init();
}
}

4. 改变列表控件外观
通过 ListCellRenderer 自定义列表项的绘制方式。
import javax.swing.*;
import java.awt.*;
public class ListCellRendererTest {
private JFrame mainWin = new JFrame("好友列表");
private String[] friends = {"李清照", "苏格拉底", "李白", "弄玉", "虎头"};
private JList friendsList = new JList(friends);
public void init() {
friendsList.setCellRenderer(new MyRenderer());
mainWin.add(friendsList);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
private class MyRenderer extends JPanel implements ListCellRenderer {
private String name;
private ImageIcon icon;
private Color backGround;
private Color forceGround;
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
this.name = value.toString();
this.icon = new ImageIcon("E:\\GUISwing\\img\\tree\\" + name + ".gif");
this.backGround = isSelected ? list.getSelectionBackground() : list.getBackground();
this.forceGround = isSelected ? list.getSelectionForeground() : list.getForeground();
return this;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(60, 80);
}
@Override
public void paint(Graphics g) {
int imageWidth = icon.getImage().getWidth(null);
int imageHeight = icon.getImage().getHeight(null);
g.setColor(backGround);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(icon.getImage(), getWidth() / 2 - imageWidth / 2, 10, null);
g.setColor(forceGround);
g.setFont(new Font("StSong", Font.BOLD, 18));
g.drawString(this.name, getWidth() / 2 - this.name.length() * 20 / 2, imageHeight + 30);
}
}
public static void main(String[] args) {
new ListCellRendererTest().init();
}
}

七、Swing 表格
1. 简单表格
import javax.swing.*;
import java.awt.*;
public class SimpleTable {
JFrame jf = new JFrame("简单表格");
Object[] titles = {"姓名", "年龄", "性别"};
Object[][] data = {
{"李清照", 29, "女"},
{"苏格拉底", 56, "男"},
{"李白", 35, "男"},
{"弄玉", 18, "女"},
{"虎头", 2, "男"}
};
public void init() {
JTable jTable = new JTable(data, titles);
jf.add(new JScrollPane(jTable));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new SimpleTable().init();
}
}
2. TableModel
自定义 TableModel 实现更灵活的数据交互。
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
public class TableModelTest {
JFrame jf = new JFrame("TableModel 演示");
Object[] titles = {"姓名", "年龄", "性别"};
Object[][] data = {
{"李清照", 29, "女"},
{"苏格拉底", 56, "男"},
{"李白", 35, "男"},
{"弄玉", 18, "女"},
{"虎头", 2, "男"}
};
private Vector titlesV = new Vector();
private Vector<Vector> dataV = new Vector<>();
public void init() {
for (int i = 0; i < titles.length; i++) titlesV.add(titles[i]);
for (int i = 0; i < data.length; i++) {
Vector t = new Vector<>();
for (int j = 0; j < data[i].length; j++) t.add(data[i][j]);
dataV.add(t);
}
MyTableModel myTableModel = new MyTableModel();
JTable jTable = new JTable(myTableModel);
jf.add(new JScrollPane(jTable));
JButton btn = new JButton("获取选中行数据");
btn.addActionListener(e -> {
int selectedColumn = jTable.getSelectedColumn();
int selectedRow = jTable.getSelectedRow();
System.out.println("当前选中行的索引:" + selectedRow);
System.out.println("当前选中列的索引:" + selectedColumn);
Object value = myTableModel.getValueAt(selectedRow, selectedColumn);
System.out.println("当前选中行第一个单元格的内容为:" + value);
});
jf.add(btn, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
private class MyTableModel extends AbstractTableModel {
@Override
public int getRowCount() { return dataV.size(); }
@Override
public int getColumnCount() { return titlesV.size(); }
@Override
public Object getValueAt(int rowIndex, int columnIndex) { return dataV.get(rowIndex).get(columnIndex); }
@Override
public String getColumnName(int column) { return (String) titlesV.get(column); }
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) { return false; }
}
public static void main(String[] args) {
new TableModelTest().init();
}
}
public class DefaultTableModelTest {
JFrame jf = new JFrame("TableModel 演示");
Object[] titles = {"姓名", "年龄", "性别"};
Object[][] data = {
{"李清照", 29, "女"},
{"苏格拉底", 56, "男"},
{"李白", 35, "男"},
{"弄玉", 18, "女"},
{"虎头", 2, "男"}
};
private Vector titlesV = new Vector();
private Vector<Vector> dataV = new Vector<>();
public void init() {
for (int i = 0; i < titles.length; i++) titlesV.add(titles[i]);
for (int i = 0; i < data.length; i++) {
Vector t = new Vector<>();
for (int j = 0; j < data[i].length; j++) t.add(data[i][j]);
dataV.add(t);
}
DefaultTableModel model = new DefaultTableModel(dataV, titlesV);
JTable jTable = new JTable(model);
JButton addRow = new JButton("添加一行");
JButton addColum = new JButton("添加一列");
JButton deleteRow = new JButton("删除一行");
addRow.addActionListener(e -> model.addRow(new Object[]{"胡歌", 30, "男"}));
deleteRow.addActionListener(e -> {
int selectedRow = jTable.getSelectedRow();
model.removeRow(selectedRow);
});
addColum.addActionListener(e -> model.addColumn("职业"));
JPanel panel = new JPanel();
panel.add(addRow);
panel.add(addColum);
panel.add(deleteRow);
jf.add(panel, BorderLayout.SOUTH);
jf.add(new JScrollPane(jTable));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new DefaultTableModelTest().init();
}
}

八、Swing 树
1. 创建树
使用 DefaultMutableTreeNode 构建树形结构。
import javax.swing.*;
import java.awt.*;
public class SimpleJTree {
JFrame jf = new JFrame("简单树");
public void init() {
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangDong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangXi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foShan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shanTou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guiLin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanNing = new DefaultMutableTreeNode("南宁");
root.add(guangDong);
root.add(guangXi);
guangDong.add(foShan);
guangDong.add(shanTou);
guangXi.add(guiLin);
guangXi.add(nanNing);
JTree tree = new JTree(root);
jf.add(tree);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new SimpleJTree().init();
}
}
2. 编辑树
编辑树节点
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class EditTree {
JFrame jf = new JFrame("可编辑结点的树");
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
JButton addSiblingBtn = new JButton("添加兄弟结点");
JButton addChildBtn = new JButton("添加子结点");
JButton deleteBtn = new JButton("删除结点");
JButton editBtn = new JButton("编辑当前结点");
public void init() {
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
JTree tree = new JTree(root);
tree.setEditable(true);
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
addSiblingBtn.addActionListener(e -> {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (selectedNode == null) return;
DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) selectedNode.getParent();
if (parentNode == null) return;
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");
int index = parentNode.getIndex(selectedNode);
model.insertNodeInto(newNode, parentNode, index);
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
TreePath treePath = new TreePath(pathToRoot);
tree.scrollPathToVisible(treePath);
tree.updateUI();
});
addChildBtn.addActionListener(e -> {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (selectedNode == null) return;
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");
selectedNode.add(newNode);
TreeNode[] pathToRoot = model.getPathToRoot(newNode);
TreePath treePath = new TreePath(pathToRoot);
tree.scrollPathToVisible(treePath);
tree.updateUI();
});
deleteBtn.addActionListener(e -> {
DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (selectedNode != null && selectedNode.getParent() != null) {
model.removeNodeFromParent(selectedNode);
}
});
editBtn.addActionListener(e -> {
TreePath selectionPath = tree.getSelectionPath();
if (selectionPath != null) {
tree.startEditingAtPath(selectionPath);
}
});
jf.add(new JScrollPane(tree));
JPanel panel = new JPanel();
panel.add(addSiblingBtn);
panel.add(addChildBtn);
panel.add(deleteBtn);
panel.add(editBtn);
jf.add(panel, BorderLayout.SOUTH);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public static void main(String[] args) {
new EditTree().init();
}
}

监听节点事件
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
public class ListenerJTree {
JFrame jf = new JFrame("监听树的选择事件");
JTree tree;
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
JTextArea eventTxt = new JTextArea(5, 20);
public void init() {
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
tree = new JTree(root);
TreeSelectionModel selectionModel = tree.getSelectionModel();
selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeSelectionListener(e -> {
TreePath newLeadSelectionPath = e.getNewLeadSelectionPath();
eventTxt.append(newLeadSelectionPath.toString() + "\n");
});
Box box = Box.createHorizontalBox();
box.add(new JScrollPane(tree));
box.add(new JScrollPane(eventTxt));
jf.add(box);
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new ListenerJTree().init();
}
}
3. 改变树外观
通过 TreeCellRenderer 自定义节点图标和颜色。
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
public class ChangeAllCellRender {
JFrame jf = new JFrame("改变所有结点外观");
JTree tree;
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
DefaultMutableTreeNode guangxi = new DefaultMutableTreeNode("广西");
DefaultMutableTreeNode foshan = new DefaultMutableTreeNode("佛山");
DefaultMutableTreeNode shantou = new DefaultMutableTreeNode("汕头");
DefaultMutableTreeNode guilin = new DefaultMutableTreeNode("桂林");
DefaultMutableTreeNode nanning = new DefaultMutableTreeNode("南宁");
public void init() {
guangdong.add(foshan);
guangdong.add(shantou);
guangxi.add(guilin);
guangxi.add(nanning);
root.add(guangdong);
root.add(guangxi);
tree = new JTree(root);
DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
renderer.setBackgroundNonSelectionColor(new Color(220, 220, 220));
renderer.setBackgroundSelectionColor(new Color(140, 140, 140));
renderer.setBorderSelectionColor(Color.BLACK);
renderer.setClosedIcon(new ImageIcon("E:\\GUISwing\\img\\tree\\close.gif"));
renderer.setFont(new Font("StSong", Font.BOLD, 16));
renderer.setLeafIcon(new ImageIcon("E:\\GUISwing\\img\\tree\\leaf.png"));
renderer.setOpenIcon(new ImageIcon("E:\\GUISwing\\img\\tree\\open.gif"));
renderer.setTextNonSelectionColor(new Color(255, 0, 0));
renderer.setTextSelectionColor(new Color(0, 0, 255));
tree.setCellRenderer(renderer);
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new ChangeAllCellRender().init();
}
}
扩展 DefaultTreeCellRenderer 可实现更复杂的图标映射。
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
public class ExtendsDefaultCellTreeRenderer {
JFrame jf = new JFrame("根据结点类型定义图标");
JTree tree;
ImageIcon rootIcon = new ImageIcon("E:\\GUISwing\\img\\tree\\root.gif");
ImageIcon databaseIcon = new ImageIcon("E:\\GUISwing\\img\\tree\\database.gif");
ImageIcon tableIcon = new ImageIcon("E:\\GUISwing\\img\\tree\\table.gif");
ImageIcon columnIcon = new ImageIcon("E:\\GUISwing\\img\\tree\\column.gif");
ImageIcon indexIcon = new ImageIcon("E:\\GUISwing\\img\\tree\\index.gif");
class NodeData {
public ImageIcon icon;
public String name;
public NodeData(ImageIcon icon, String name) {
this.icon = icon;
this.name = name;
}
}
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new NodeData(rootIcon, "数据库导航"));
DefaultMutableTreeNode salaryDb = new DefaultMutableTreeNode(new NodeData(databaseIcon, "公司工资数据库"));
DefaultMutableTreeNode customerDb = new DefaultMutableTreeNode(new NodeData(databaseIcon, "公司客户数据库"));
DefaultMutableTreeNode employee = new DefaultMutableTreeNode(new NodeData(tableIcon, "员工表"));
DefaultMutableTreeNode attend = new DefaultMutableTreeNode(new NodeData(tableIcon, "考勤表"));
DefaultMutableTreeNode concat = new DefaultMutableTreeNode(new NodeData(tableIcon, "联系方式表"));
DefaultMutableTreeNode id = new DefaultMutableTreeNode(new NodeData(indexIcon, "员工 ID"));
DefaultMutableTreeNode name = new DefaultMutableTreeNode(new NodeData(columnIcon, "姓名"));
DefaultMutableTreeNode gender = new DefaultMutableTreeNode(new NodeData(columnIcon, "性别"));
public void init() {
root.add(salaryDb);
root.add(customerDb);
salaryDb.add(employee);
salaryDb.add(attend);
customerDb.add(concat);
concat.add(id);
concat.add(name);
concat.add(gender);
tree = new JTree(root);
tree.setCellRenderer(new MyRenderer());
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
private class MyRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
NodeData nodeData = (NodeData) node.getUserObject();
this.setText(nodeData.name);
this.setIcon(nodeData.icon);
return this;
}
}
public static void main(String[] args) {
new ExtendsDefaultCellTreeRenderer().init();
}
}
完全自定义 TreeCellRenderer 接口可实现像素级控制。
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeCellRenderer;
import java.awt.*;
public class CustomerTreeNode {
JFrame jf = new JFrame("定制树的结点");
JTree tree;
DefaultMutableTreeNode friends = new DefaultMutableTreeNode("我的好友");
DefaultMutableTreeNode qingzhao = new DefaultMutableTreeNode("李清照");
DefaultMutableTreeNode suge = new DefaultMutableTreeNode("苏格拉底");
DefaultMutableTreeNode libai = new DefaultMutableTreeNode("李白");
DefaultMutableTreeNode nongyu = new DefaultMutableTreeNode("弄玉");
DefaultMutableTreeNode hutou = new DefaultMutableTreeNode("虎头");
public void init() {
friends.add(qingzhao);
friends.add(suge);
friends.add(libai);
friends.add(nongyu);
friends.add(hutou);
tree = new JTree(friends);
MyRenderer renderer = new MyRenderer();
tree.setCellRenderer(renderer);
jf.add(new JScrollPane(tree));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
private class MyRenderer extends JPanel implements TreeCellRenderer {
private ImageIcon icon;
private String name;
private Color background;
private Color foreground;
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
this.icon = new ImageIcon("E:\\GUISwing\\img\\tree\\" + value.toString() + ".gif");
this.name = value.toString();
this.background = hasFocus ? new Color(144, 200, 225) : new Color(255, 255, 255);
this.foreground = hasFocus ? new Color(255, 255, 3) : new Color(0, 0, 0);
return this;
}
@Override
public Dimension getPreferredSize() {
return new Dimension(80, 80);
}
@Override
public void paint(Graphics g) {
int iconWidth = this.icon.getIconWidth();
int iconHeight = this.icon.getIconHeight();
g.setColor(background);
g.fillRect(0, 0, getWidth(), getHeight());
g.drawImage(this.icon.getImage(), getWidth() / 2 - iconWidth / 2, 10, null);
g.setColor(foreground);
g.setFont(new Font("StSong", Font.BOLD, 18));
g.drawString(this.name, getWidth() / 2 - this.name.length() * 20 / 2, iconHeight + 30);
}
}
public static void main(String[] args) {
new CustomerTreeNode().init();
}
}
九、Swing 控件修饰
1. 控件边框
使用 BorderFactory 可以快速为组件添加各种边框效果。
import javax.swing.*;
import java.awt.*;
public class BorderTest {
JFrame jf = new JFrame("测试边框");
public void init() {
jf.setLayout(new GridLayout(2, 4));
jf.add(getJPanelWithBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.RED, Color.GREEN, Color.BLUE, Color.GRAY), "BevelBorder"));
jf.add(getJPanelWithBorder(BorderFactory.createLineBorder(Color.ORANGE, 10), "LineBorder"));
jf.add(getJPanelWithBorder(BorderFactory.createEmptyBorder(10, 5, 20, 10), "EmptyBorder"));
jf.add(getJPanelWithBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.RED, Color.GREEN), "EtchedBorder"));
jf.add(getJPanelWithBorder(new TitledBorder(new LineBorder(Color.ORANGE, 10), "测试标题", TitledBorder.LEFT, TitledBorder.BOTTOM, new Font("StSong", Font.BOLD, 18), Color.BLUE), "TitledBorder"));
jf.add(getJPanelWithBorder(new MatteBorder(10, 5, 20, 10, Color.GREEN), "MatteBorder"));
jf.add(getJPanelWithBorder(new CompoundBorder(new LineBorder(Color.RED, 10), new TitledBorder(new LineBorder(Color.ORANGE, 10), "测试标题")), "CompoundBorder"));
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public JPanel getJPanelWithBorder(Border border, String content) {
JPanel jPanel = new JPanel();
jPanel.add(new JLabel(content));
jPanel.setBorder(border);
return jPanel;
}
public static void main(String[] args) {
new BorderTest().init();
}
}

相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online