实际使用 Java 开发图形界面程序时 ,很少使用 AWT 组件,绝大部分时候都是用 Swing 组件开发的。
Swing是由100%纯 Java实现的,不再依赖于本地平台的 GUI, 因此可以在所有平台上都保持相同的界面外观。
独立于本地平台的Swing组件被称为轻量级组件;而依赖于本地平台的 AWT 组件被称为重量级组件。
由于 Swing 的所有组件完全采用 Java 实现,不再调用本地平台的 GUI,所以导致 Swing 图形界面的显示速度要比 AWT 图形界面的显示速度慢一些,但相对于快速发展的硬件设施而言,这种微小的速度差别无妨大碍。
使用Swing的优势:
Swing 组件采用 MVC(Model-View-Controller), 即(模型一视图一控制器)设计模式:
模型(Model):用于维护组件的各种状态;
视图(View):是组件的可视化表现;
控制器(Controller):用于控制对于各种事件、组件做出响应 。
当模型发生改变时,它会通知所有依赖它的视图,视图会根据模型数据来更新自己。Swing使用UI代理来包装视图和控制器, 还有一个模型对象来维护该组件的状态。例如,按钮JButton
有一个维护其状态信息的模型ButtonModel
对象。 Swing组件的模型是自动设置的,因此一般都使用JButton,而无须关心ButtonModel对象。
大部分Swing 组件都是 JComponent
抽象类的直接或间接子类(并不是全部的 Swing 组件),JComponent
类定义了所有子类组件的通用方法 ,JComponent
类是 AWT 里 java.awt.Container
类的子类 ,这也是 AWT 和 Swing 的联系之一。 绝大部分 Swing 组件类继承了Container
类,所以Swing 组件都可作为容器使用( JFrame继承了Frame 类)。
大部分情况下,只需要在AWT组件的名称前面加个J
,就可以得到其对应的Swing组件名称,但有几个例外:
JComboBox
: 对应于 AWT 里的 Choice
组件,但比 Choice
组件功能更丰富 。JFileChooser
: 对应于 AWT 里的 FileDialog
组件 。JScrollBar
: 对应于 AWT 里的 Scrollbar
组件,注意两个组件类名中 b
字母的大小写差别。JCheckBox
: 对应于 AWT 里的 Checkbox
组件, 注意两个组件类名中 b
字母的大小 写差别 。JCheckBoxMenultem
: 对应于 AWT 里的 CheckboxMenuItem
组件,注意两个组件类名中 b
字母的大小写差别。JFrame
、JApplet
、JDialog
和 JWindow
。JPanel
、 JScrollPane
、 JSplitPane
、 JToolBar
等 。JIntemalFrame
、 JRootPane
、 JLayeredPane
和 JDestopPane
等 。JButton
、 JComboBox
、 JList
、 JMenu
、 JSlider
等 。JLabel
、 JProgressBar
和 JToolTip
等。JTable
、 JTextArea
和JTextField
等 。JColorChooser
和 JFileChooser
等。Swing 为除 Canvas 之外的所有 AWT 组件提供了相应的实现,Swing 组件比 AWT 组件的功能更加强大。相对于 AWT 组件, Swing 组件具有如下 4 个额外的功能:
setToolTipText()
方法,为组件设置对用户有帮助的提示信息 。Icon
接口提供了 一个实现类: Imagelcon
,该实现类代表一个图像图标。JComponent
对象都有一个相应的 ComponentUI
对象,为它完成所有的绘画、事件处理、决定尺寸大小等工作。 ComponentUI
对象依赖当前使用的 PLAF , 使用 UIManager.setLookAndFeel()
方法可以改变图形界面的外观风格 。组件名.setAccelerator(keyStroke.getKeyStroke("大写字母",InputEvent.CTRL_MASK))
方法来设置,其中KeyStroke
代表一次击键动作,可以直接通过按键对应字母来指定该击键动作 。JFrame
的风格时,调用了 SwingUtilities.updateComponentTreeUI(f.getContentPane());
这是因为如果直接更新 JFrame
本身 ,将会导致 JFrame
也被更新, JFrame
是一个特殊的容器 , JFrame 依然部分依赖于本地平台的图形组件 。如果强制 JFrame
更新,则有可能导致该窗口失去标题栏和边框 。setComponentPopupMenu()
方法即可,更简单。JFrame
窗口,也无需监听器,只需要调用setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
方法即可,更简单。JScrollPane
中,然后使用JScrollPane
即可。每个 Swing 组件都有一个对应的UI 类,例如 JButton
组件就有一个对应的 ButtonUI
类来作为UI代理 。每个Swing组件的UI代理的类名总是将该 Swing组件类名的 J
去掉,然后在后面添加 UI
后缀 。 UI
代理类通常是一个抽象基类 ,不同的 PLAF
会有不同的UI代理实现类 。 Swing 类库中包含了几套UI代理,分别放在不同的包下, 每套UI代理都几乎包含了所有 Swing组件的 ComponentUI
实现,每套这样的实现都被称为一种PLAF
实现 。以 JButton
为例,其 UI 代理的继承层次下图:
如果需要改变程序的外观风格, 则可以使用如下代码:
JFrame jf = new JFrame();
try {
//设置外观风格
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
//刷新jf容器及其内部组件的外观
SwingUtilities.updateComponentTreeUI(jf);
} catch (Exception e) {
e.printStackTrace();
}
具体的使用如下:
public class LookAndFeelDemo {
private JFrame jFrame = new JFrame("LookAndFeelDemo");
private JTextArea jTextArea = new JTextArea(10,30);
private JTextField jTextField = new JTextField(30);
private ButtonGroup buttonGroup = new ButtonGroup();
private JCheckBox boy = new JCheckBox("男",true);
private JCheckBox girl = new JCheckBox("女",false);
private JComboBox<String> jComboBox = new JComboBox();
private JList<String> jList = new JList<>();
private JButton jButton = new JButton("确定");
private void init(){
buttonGroup.add(boy);
buttonGroup.add(girl);
Box bottom = Box.createHorizontalBox();
bottom.add(boy);
bottom.add(girl);
jComboBox.addItem("Metal 风格");
jComboBox.addItem("Nimbus 风格");
jComboBox.addItem("Windows 风格");
jComboBox.addItem("Windows 经典风格");
jComboBox.addItem("Motif 风格");
bottom.add(jComboBox);
bottom.add(jButton);
Box center = Box.createVerticalBox();
jTextArea.setBorder(BorderFactory.createLineBorder(Color.BLACK));
center.add(jTextArea);
center.add(jTextField);
jList.setListData(new String[]{"Windows","Linux","MacOS"});
jList.setBorder(BorderFactory.createLineBorder(Color.BLACK));
jFrame.add(center,BorderLayout.CENTER);
jFrame.add(bottom,BorderLayout.SOUTH);
jFrame.add(jList,BorderLayout.EAST);
jButton.addActionListener(e -> {
String actionCommand = jComboBox.getActionCommand();
changeLookAndFeel(actionCommand);
});
jFrame.pack();
jFrame.setVisible(true);
}
private void changeLookAndFeel(String actionCommand){
try {
switch (actionCommand){
case "Metal 风格":
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
break;
case "Nimbus 风格":
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
break;
case "Windows 风格":
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
break;
case "Windows 经典风格":
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel");
break;
case "Motif 风格":
UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
break;
}
SwingUtilities.updateComponentTreeUI(jFrame.getContentPane());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new LookAndFeelDemo().init();
}
}
在Java Swing中,图标(Icon)通常用于按钮、标签和其他组件上以增强用户界面。Java Swing提供了多种方式来使用图标,包括直接使用Icon
接口、使用ImageIcon
类,以及使用Swing的内置图标类。
public class IconDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("IconDemo");
// 从 classpath 下加载图标
ImageIcon imageIcon = new ImageIcon(IconDemo.class.getClassLoader().getResource("user.png"));
JButton jButton = new JButton("图标按钮",imageIcon);
jFrame.add(jButton);
jFrame.pack();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
Swing提供了一些内置的图标类,如UIManager
中的图标,这些图标通常用于标准的UI组件。
public class IconDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("IconDemo");
// 获取内置图标,例如 Metal 主题的文件夹图标
Icon icon = MetalIconFactory.getTreeFolderIcon();
JButton jButton = new JButton("图标按钮",icon);
jFrame.add(jButton);
jFrame.pack();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}
很多情况下,我们常常喜欢给不同的组件设置边框,从而让界面的层次感更明显,swing中提供了Border对象来代表一个边框,下图是Border的继承体系图
TitledBorder
:它的作用并不是直接为其他组件添加边框,而是为其他边框设置标题,创建该类的对象时,需要传入一个其他的Border对象ComoundBorder
:用来组合其他两个边框,创建该类的对象时,需要传入其他两个Border对象,一个作为内边框,一个作为外边框BorderFactory
或者XxxBorder
创建Border
的实例对象;setBorder(Border b)
方法为组件设置边框;public class BorderDemo {
JFrame jf = new JFrame("测试边框");
public void init(){
//设置Jframe为网格布局
jf.setLayout(new GridLayout(2,4));
//创建凸起的斜边框,分别设置四条边的颜色
Border bb = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.RED,Color.GREEN,Color.BLUE,Color.GRAY);
jf.add(getPanelWithBorder(bb,"BevelBorder"));
//创建LineBorder
Border lb = BorderFactory.createLineBorder(Color.ORANGE, 10);
jf.add(getPanelWithBorder(lb,"LineBorder"));
//创建EmptyBorder,会在组件的四周留白
Border eb = BorderFactory.createEmptyBorder(20, 5, 10, 30);
jf.add(getPanelWithBorder(eb,"EmptyBorder"));
//创建EtchedBorder,
Border etb = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.RED, Color.GREEN);
jf.add(getPanelWithBorder(etb,"EtchedBorder"));
//创建TitledBorder,为原有的Border添加标题
TitledBorder tb = new TitledBorder(lb,"测试标题",TitledBorder.LEFT,TitledBorder.BOTTOM,new Font("StSong",Font.BOLD,18),Color.BLUE);
jf.add(getPanelWithBorder(tb,"TitledBorder"));
//直接创建MatteBorder,它是EmptyBorder的子类,EmptyBorder是留白,而MatteBorder可以给留空的区域填充颜色
MatteBorder mb = new MatteBorder(20,5,10,30,Color.GREEN);
jf.add(getPanelWithBorder(mb,"MatteBorder"));
//直接创创建CompoundBorder,将两个边框组合成新边框
CompoundBorder cb = new CompoundBorder(new LineBorder(Color.RED,8),tb);
jf.add(getPanelWithBorder(cb,"CompoundBorder"));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
public JPanel getPanelWithBorder(Border border,String borderName){
JPanel jPanel = new JPanel();
jPanel.add(new JLabel(borderName));
//为panel设置边框
jPanel.setBorder(border);
return jPanel;
}
public static void main(String[] args) {
new BorderDemo().init();
}
}
Swing 提供了JToolBar类来创建工具条,并且可以往JToolBar中添加多个工具按钮。
方法名称 | 方法功能 |
---|---|
JToolBar(String name,int orientation) |
创建一个名字为name ,方向为orientation 的工具条对象,其orientation 的是取值可以是SwingConstants.HORIZONTAL 或SwingConstants.VERTICAL |
JButton add(Action a) |
通过Action对象为JToolBar工具条添加对应的工具按钮 |
addSeparator(Dimension size) |
向工具条中添加指定大小的分隔符 |
setFloatable(boolean b) |
设定工具条是否可以被拖动 |
setMargin(Insets m) |
设置工具条与工具按钮的边距 |
setOrientation(int o) |
设置工具条的方向 |
setRollover(boolean rollover) |
设置此工具条的rollover状态 |
public class JToolBarDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JToolBarDemo");
// 创建一个文本域
JTextArea jTextArea = new JTextArea(6,30);
// 给文本域设置滚动条
JScrollPane jScrollPane = new JScrollPane(jTextArea);
// 创建工具条
JToolBar jToolBar = new JToolBar();
// 创建两个按钮
Action pre = new AbstractAction("上一页") {
@Override
public void actionPerformed(ActionEvent e) {
jTextArea.append("上一页\n");
}
};
Action next = new AbstractAction("下一页") {
@Override
public void actionPerformed(ActionEvent e) {
jTextArea.append("下一页\n");
}
};
// 将按钮添加到工具条中
jToolBar.add(pre);
jToolBar.add(next);
jFrame.add(jToolBar, BorderLayout.NORTH);
jFrame.add(jScrollPane, BorderLayout.CENTER);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
JColorChooser
用于创建颜色选择器对话框,该类的用法非常简单,只需要调用它的静态方法就可以快速生成一个颜色选择对话框
/*
参数:
componet:指定当前对话框的父组件
title:当前对话框的名称
initialColor:指定默认选中的颜色
返回值:
返回用户选中的颜色
*/
public static Color showDialog(Component component,String title,Color initialColor)
public class JColorChooserDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JColorChooserDemo");
JTextArea jTextArea = new JTextArea(6,30);
JButton jButton = new JButton("选择颜色");
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Color result = JColorChooser.showDialog(jFrame, "选择颜色", jTextArea.getBackground());
if (result != null) {
jTextArea.setBackground(result);
}
}
});
jFrame.add(jTextArea);
jFrame.add(jButton, BorderLayout.SOUTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
JFileChooser 的功能与AWT中的 FileDialog 基本相似,也是用于生成"打开文件"、"保存文件"对话框。与 FileDialog 不同的是 , JFileChooser 无须依赖于本地平台的 GUI,它由 100%纯 Java 实现,在所有平台 上具有完全相同的行为,并可以在所有平台上具有相同的外观风格。
JFileChooser使用步骤如下:
1、创建JFileChooser对象
JFileChooser chooser = new JFileChooser("D:\\a");//指定默认打开的本地磁盘路径
2、调用JFileChooser的方法,进行初始化
// 设定默认选中的文件
setSelectedFile(File file);
// 设定默认选中的多个文件
setSelectedFiles(File[] selectedFiles);
// 设置是否允许多选,默认是单选
setMultiSelectionEnabled(boolean b);
// 设置可以选择内容,例如文件、文件夹等,默认只能选择文件
setFileSelectionMode(int mode):
3、打开文件对话框
// 打开文件加载对话框,并指定父组件
showOpenDialog(Component parent);
// 打开文件保存对话框,并指定父组件
showSaveDialog(Component parent);
4、获取用户选择的结果
// 获取用户选择的一个文件
File getSelectedFile();
// 获取用户选择的多个文件
File[] getSelectedFiles();
showOpenDialog()
和showSaveDialog()
返回以下常量:
JFileChooser.APPROVE_OPTION
:用户点击确认按钮(如“打开”或“确定”),表示成功选择文件。JFileChooser.CANCEL_OPTION
:用户点击取消按钮或关闭对话框,表示放弃选择。JFileChooser.ERROR_OPTION
:对话框因异常关闭(如权限问题),表示操作失败。public class IconDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("IconDemo");
JFileChooser jFileChooser = new JFileChooser(".");
JButton b1 = new JButton("选择文件");
JButton b2 = new JButton("选择文件夹");
JButton b3 = new JButton("选择图片");
JButton b4 = new JButton("保存文件");
b1.addActionListener(e -> {
jFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (jFileChooser.showOpenDialog(jFrame) == JFileChooser.APPROVE_OPTION) {
System.out.println("文件路径:" + jFileChooser.getSelectedFile().getAbsolutePath());
}
});
b2.addActionListener(e -> {
jFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
if (jFileChooser.showOpenDialog(jFrame) == JFileChooser.APPROVE_OPTION) {
System.out.println("文件夹路径:" + jFileChooser.getSelectedFile().getAbsolutePath());
}
});
b3.addActionListener(e -> {
jFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
jFileChooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().endsWith(".jpg") || f.getName().endsWith(".png");
}
@Override
public String getDescription() {
return "图片文件";
}
});
if (jFileChooser.showOpenDialog(jFrame) == JFileChooser.APPROVE_OPTION) {
System.out.println("文件路径:" + jFileChooser.getSelectedFile().getAbsolutePath());
}
});
b4.addActionListener(e -> {
jFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (jFileChooser.showSaveDialog(jFrame) == JFileChooser.APPROVE_OPTION) {
System.out.println("文件路径:" + jFileChooser.getSelectedFile().getAbsolutePath());
}
});
Box verticalBox = Box.createVerticalBox();
verticalBox.add(b1);
verticalBox.add(b2);
verticalBox.add(b3);
verticalBox.add(b4);
jFrame.add(verticalBox);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
通过 JOptionPane 可以非常方便地创建一些简单的对话框, Swing 已经为这些对话框添加了相应的组件,无须程序员手动添加组件。 JOptionPane 提供了如下 4 个方法来创建对话框 。
方法名称 | 方法功能 |
---|---|
showMessageDialog showInternalMessageDialog |
消息对话框 ,告知用户某事己发生 , 用户只能单击"确定"按钮 , 类似于 JavaScript 的 alert 函数 。 |
showConfirmDialog showInternalConfirmDialog |
确认对话框,向用户确认某个问题,用户可以选择 yes 、 no 、 cancel 等选项 。 类似于 JavaScript 的 comfirm 函数 。该方法返回用户单击了 哪个按钮 |
showInputDialog showInternalInputDialog |
输入对话框,提示要求输入某些信息,类似于 JavaScript的 prompt 函数。该方法返回用户输入的字符串 。 |
showOptionDialog showInternalOptionDialog |
自定义选项对话框 ,允许使用自 定义选项 ,可以取代showConfirmDialog 所产生的对话框,只是用起来更复杂 。 |
上述方法都有都有很多重载形式,选择其中一种最全的形式,参数解释如下:
showXxxDialog(
Component parentComponent,
Object message,
String title,
int optionType,
int messageType,
Icon icon,
Object[] options,
Object initialValue
)
parentComponent
:当前对话框的父组件message
:对话框上显示的信息,信息可以是字符串、组件、图片等title
:当前对话框的标题optionType
:当前对话框上显示的按钮类型(DEFAULT_OPTION
、YES_NO_OPTION
、YES_NO_CANCEL_OPTION
、OK_CANCEL_OPTION
)messageType
:当前对话框的类型(ERROR_MESSAGE
、INFORMATION_MESSAGE
、WARNING_MESSAGE
、QUESTION_MESSAGE
、PLAIN_MESSAGE
)icon
:当前对话框左上角的图标options
:自定义下拉列表的选项initialValue
:自定义选项中的默认选中项当用户与对话框交互结束后,不同类型对话框的返回值如下:
showMessageDialog
:无返回值 。showlnputDialog
:返回用户输入或选择的字符串 。showConfirmDialog
:返回一个整数代表用户选择的选项 。showOptionDialog
:返回一个整数代表用户选择的选项,如果用户选择第一项,则返回 0; 如果选择第二项,则返回1……依此类推 。对 showConfirmDialog
所产生的对话框,有如下几个返回值:
YES_OPTION
:用户单击了"是"按钮后返回 。NO_OPTION
:用户单击了"否"按钮后返回 。CANCEL_OPTION
:用户单击了"取消"按钮后返回 。OK_OPTION
:用户单击了"确定"按钮后返回 。CLOSED_OPTION
:用户单击了对话框右上角的"X"按钮后返回。public class MessageDialogDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("MessageDialogDemo");
JButton b1 = new JButton("ERROR_MESSAGE");
JButton b2 = new JButton("INFORMATION_MESSAGE");
JButton b3 = new JButton("WARNING_MESSAGE");
JButton b4 = new JButton("QUESTION_MESSAGE");
JButton b5 = new JButton("PLAIN_MESSAGE");
ActionListener actionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
switch (e.getActionCommand()) {
case "ERROR_MESSAGE":
JOptionPane.showMessageDialog(jFrame, "这是一个错误信息", "错误信息", JOptionPane.ERROR_MESSAGE);
break;
case "INFORMATION_MESSAGE":
JOptionPane.showMessageDialog(jFrame, "这是一个信息提示", "信息提示", JOptionPane.INFORMATION_MESSAGE);
break;
case "WARNING_MESSAGE":
JOptionPane.showMessageDialog(jFrame, "这是一个警告信息", "警告信息", JOptionPane.WARNING_MESSAGE);
break;
case "QUESTION_MESSAGE":
JOptionPane.showMessageDialog(jFrame, "这是一个询问信息", "询问信息", JOptionPane.QUESTION_MESSAGE);
break;
case "PLAIN_MESSAGE":
JOptionPane.showMessageDialog(jFrame, "这是一个普通信息", "普通信息", JOptionPane.PLAIN_MESSAGE);
break;
}
}
};
b1.addActionListener(actionListener);
b2.addActionListener(actionListener);
b3.addActionListener(actionListener);
b4.addActionListener(actionListener);
b5.addActionListener(actionListener);
Box verticalBox = Box.createVerticalBox();
verticalBox.add(b1);
verticalBox.add(b2);
verticalBox.add(b3);
verticalBox.add(b4);
verticalBox.add(b5);
jFrame.add(verticalBox);
jFrame.pack();
jFrame.setVisible(true);
}
}
public class ConfirmDialogDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("ConfirmDialogDemo");
JButton jButton = new JButton("确认对话框");
jButton.addActionListener(e -> {
int result = JOptionPane.showConfirmDialog(jFrame, "确认对话框", "确认对话框", JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
System.out.println("点击了确认");
} else if (result == JOptionPane.NO_OPTION) {
System.out.println("点击了取消");
}else if (result == JOptionPane.CLOSED_OPTION) {
System.out.println("点击了关闭");
}else if (result == JOptionPane.CANCEL_OPTION) {
System.out.println("点击了取消");
}else if (result == JOptionPane.OK_OPTION) {
System.out.println("点击了确定");
}
});
jFrame.add(jButton);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
public class InputDialogDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("InputDialogDemo");
JTextArea jTextArea = new JTextArea(6,30);
JButton jButton = new JButton("输入对话框");
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String result = JOptionPane.showInputDialog(jFrame, "请输入内容", "输入对话框", JOptionPane.INFORMATION_MESSAGE);
if (result != null) {
jTextArea.setText(result);
}
}
});
jFrame.add(jTextArea);
jFrame.add(jButton, BorderLayout.SOUTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
public class InputDialogDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("InputDialogDemo");
JTextArea jTextArea = new JTextArea(6,30);
JButton jButton = new JButton("输入对话框");
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Object o = JOptionPane.showInputDialog(
jFrame,
"输入对话框",
"输入框标题",
JOptionPane.INFORMATION_MESSAGE,
null,
new String[]{"选项1", "选项2", "选项3"},
"选项1");
if (o != null) {
jTextArea.setText(o.toString());
}
}
});
jFrame.add(jTextArea);
jFrame.add(jButton, BorderLayout.SOUTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
public class OptionDialogDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("OptionDialogDemo");
JButton jButton = new JButton("选项对话框");
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int i = JOptionPane.showOptionDialog(
jFrame,
"选项对话框",
"选项框标题",
JOptionPane.DEFAULT_OPTION,
JOptionPane.INFORMATION_MESSAGE,
null,
new String[]{"选项1", "选项2", "选项3"},
"选项1"
);
if (i == 0){
System.out.println("点击了选项1");
}else if (i == 1){
System.out.println("点击了选项2");
}else if (i == 2){
System.out.println("点击了选项3");
}
}
});
jFrame.add(jButton);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
Swing提供了一些具有特殊功能的容器 , 这些特殊容器可以用于创建一些更复杂的用户界面。
JSplitPane 用于创建一个分割面板,它可以将一个组件(通常是一个容器)分割成两个部分,并提供一个分割条,用户可以拖动该分割条来调整两个部分的大小。
使用步骤如下:
1、创建JSplitPane对象
JSplitPane(int newOrientation, Component newLeftComponent,Component newRightComponent);
newOrientation
:指定JSplitPane容器的分割方向
JSplitPane.VERTICAL_SPLIT
:为纵向分割;JSplitPane.HORIZONTAL_SPLIT
:为横向分割;newLeftComponent
:左侧或者上侧的组件;newRightComponent
:右侧或者下侧的组件;2、设置是否开启连续布局的支持(可选)
默认是关闭的,如果设置为true
,则打开连续布局的支持,但由于连续布局支持需要不断的重绘组件,所以效率会低一些
setContinuousLayout(boolean newContinuousLayout);
3、设置是否支持一触即展的支持(可选)
默认是关闭的,如果设置为true
,则打开"一触即展"的支持
setOneTouchExpandable(boolean newValue);
4、其他设置
//设置分隔条的位置为JSplitPane的某个百分比
setDividerLocation(double proportionalLocation);
//通过像素值设置分隔条的位置
setDividerLocation(int location);
//通过像素值设置分隔条的大小
setDividerSize(int newSize);
//设置指定位置的组件
setLeftComponent(Component comp);
setTopComponent(Component comp);
setRightComponent(Component comp);
setBottomComponent(Component comp)
public class JSplitPaneDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JSplitPaneDemo");
JTextArea t1 = new JTextArea(6,30);
JTextArea t2 = new JTextArea(6,30);
// 创建分割面板
JSplitPane jSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,t1,t2);
// 设置分割面板的位置
jSplitPane.setDividerLocation(0.5);
// 设置分割面板的连续布局
jSplitPane.setContinuousLayout(true);
// 设置分割面板的展开方式
jSplitPane.setOneTouchExpandable(true);
jFrame.add(jSplitPane);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
}
JTabbedPane可以很方便地在窗口上放置多个标签页,每个标签页相当于获得了一个与外部容器具有相同大小的组件摆放区域。
使用步骤如下:
1、创建JTabbedPane对象
JTabbedPane(int tabPlacement, int tabLayoutPolicy);
tabPlacement
:指定标签标题的放置位置,可以选择 SwingConstants
中的四个常量:TOP
、LEFT
、BOTTOM
、RIGHT
tabLaoutPolicy
:指定当窗口不能容纳标签页标题时的布局策略,可以选择JTabbedPane.WRAP_TAB_LAYOUT
和JTabbedPane.SCROLL_TAB_LAYOUT
2、通过JTabbedPane对象堆标签进行增删改查
// 添加标签
addTab(String title, Icon icon, Component component, String tip);
title
:标签的名称icon
:标签的图标component
:标签对应的组件tip
:光标放到标签上的提示// 插入标签页
insertTab(String title, Icon icon, Component component, String tip, int index);
title
:标签的名称icon
:标签的图标component
:标签对应的组件tip
:光标放到标签上的提示index
:在哪个索引处插入标签页// 修改标签页对应的组件
setComponentAt(int index, Component component);
index
:修改哪个索引处的标签component
:标签对应的组件// 删除标签
removeTabAt(int index);
index
:删除哪个索引处的标签3、设置当前显示的标签页
setSelectedIndex(int index);
4、设置JTabbedPane的其他属性
// 将指定位置的禁用图标设置为icon,该图标也可以是null表示不使用禁用图标。
setDisabledIconAt(int index, Icon disabledIcon);
// 设置指定位置的标签页是否启用。
setEnabledAt(int index, boolean enabled);
// 设置指定位置标签页的标题为 title,该title可以是null,这表明设置该标签页的标题为空。
setTitleAt(int index, String title);
// 设置指定位置标签页的提示文本。
setToolTipTextAt(int index, String toolTipText);
5、为JTabbedPane设置监听器
addChangeListener(ChangeListener l);
public class JTabbedPaneDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JTabbedPaneDemo");
JTabbedPane jTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
JButton add = new JButton("添加Tab");
add.addActionListener(e -> {
JTextArea jTextArea = new JTextArea(5,30);
jTabbedPane.add("Tab" + jTabbedPane.getTabCount(), jTextArea);
});
JButton remove = new JButton("删除Tab");
remove.addActionListener(e -> {
if (jTabbedPane.getTabCount() > 0) {
jTabbedPane.remove(jTabbedPane.getTabCount() - 1);
}
});
jFrame.add(jTabbedPane);
Box horizontalBox = Box.createHorizontalBox();
horizontalBox.add(add);
horizontalBox.add(remove);
jFrame.add(horizontalBox, BorderLayout.SOUTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(400, 300);
jFrame.setVisible(true);
}
}
JLayeredPane
是 一个代表有层次深度的容器,它允许组件在需要时互相重叠。当向JLayeredPane容器中添加组件时,需要为该组件指定一个深度索引 , 其中层次索引较高的层里的组件位于其他层的组件之上。
JLayeredPane
还将容器的层次深度分成几个默认层 ,程序只是将组件放入相应的层,从而可以更容易地确保组件的正确重叠,无须为组件指定具体的深度索引。JLayeredPane
提供了如下几个默认层:
DEFAULT_LAYER
:大多数组件位于标准层,这是最底层;PALETTE_LAYER
: 调色板层位于默认层之上 。该层对于浮动工具栏和调色板很有用,因此可以位于其他组件之上 。MODAL_LAYER
:该层用于显示模式对话框。它们将出现在容器中所有工具栏 、调色板或标准组件的上面 。POPUP_LAYER
: 该层用于显示右键菜单,与对话框 、工具提示和普通组件关联的弹出式窗口将出现在对应的对话框、工具提示和普通组件之上。DRAG_LAYER
:该层用于放置拖放过程中的组件,拖放操作中的组件位于所有组件之上。 一旦拖放操作结束后,该组件将重新分配到其所属的正常层。JLayeredPane 方法:
moveToBack(Component c)
:把当前组件c移动到所在层的所有组件的最后一个位置;moveToFront(Component c)
:把当前组件c移动到所在层的所有组件的第一个位置;setLayer(Component c, int layer)
:更改组件c所处的层;需要注意的是,往JLayeredPane
中添加组件,如果要显示,则必须手动设置该组件在容器中显示的位置以及大小。
public class JLayeredPanelDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JLayeredPanelDemo");
JLayeredPane jLayeredPane = new JLayeredPane();
jLayeredPane.add(createJPanel(Color.RED), JLayeredPane.DEFAULT_LAYER);
jLayeredPane.add(createJPanel(Color.GREEN), JLayeredPane.MODAL_LAYER);
jLayeredPane.add(createJPanel(Color.BLUE), JLayeredPane.PALETTE_LAYER);
jLayeredPane.setPreferredSize(new Dimension(300, 300));
jFrame.add(jLayeredPane);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.pack();
jFrame.setVisible(true);
}
public static JPanel createJPanel(Color backgroundColor){
JPanel jPanel = new JPanel();
jPanel.setBackground(backgroundColor);
jPanel.setSize(100, 100);
return jPanel;
}
}
JDesktopPane
是JLayeredPane
的子类,这种容器在开发中会更常用很多应用程序都需要启动多个内部窗口来显示信息(典型的比如IDEA、NotePad++),这些内部窗口都属于同一个外部窗口,当外部窗口最小化时,这些内部窗口都被隐藏起来。
在 Windows 环境中,这种用户界面被称为多文档界面 (Multiple Document Interface, MDI)。
使用 Swing 可以非常简单地创建出这种 MDI 界面,通常,内部窗口有自己的标题栏、标题、图标、三个窗口按钮,并允许拖动改变内部窗口的大小和位置,但内部窗口不能拖出外部窗口。
JDesktopPane
需要和 JInternalFrame
结合使用,其中JDesktopPane
代表一个虚拟桌面 ,而JInternalFrame
则用于创建内部窗口。使用 JDesktopPane
和 JInternalFrame
创建内部窗口按如下步骤进行即可:
1、创建 一 个 JDesktopPane
对象,代表虚拟桌面
JDesktopPane()
2、使用 JInternalFrame
创建一个内部窗口
JInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable);
title
:内部窗口标题resizable
:是否可改变大小closeble
: 是否可关闭maximizable
: 是否可最大化iconifiable
:是否可最小化3、一旦获得了内部窗口之后,该窗口的用法和普通窗口的用法基本相似, 一样可以指定该窗口的布局管理器, 一样可以向窗口内添加组件、改变窗口图标等。
4、将该内部窗口以合适大小、在合适位置显示出来。与普通窗口类似的是, 该窗口默认大小是0x0
像素,位于(0,0)
位置(虚拟桌面的左上角处),并且默认处于隐藏状态,程序可以通过如下代码将内部窗口显示出来:
// 设置内部窗口的大小以及在外部窗口中的位置
reshape(int x, int y, int width, int height);
// 设置内部窗口可见
show();
5、将内部窗口JInternalFrame
添加到 JDesktopPane
容器中,再将 JDesktopPane
容器添加到其他容器中。
public class JInternalFrameDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JInternalFrameDemo");
JButton jButton = new JButton("创建内部窗口");
JDesktopPane jDesktopPane = new JDesktopPane();
jButton.addActionListener(e -> {
JInternalFrame jInternalFrame = new JInternalFrame("内部窗口",true,true,true,true);
jInternalFrame.setSize(300,200);
jDesktopPane.add(jInternalFrame);
jInternalFrame.setVisible(true);
});
jFrame.add(jDesktopPane);
jFrame.add(jButton, BorderLayout.SOUTH);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(300,300);
jFrame.setVisible(true);
}
}
使用JProgressBar创建进度条的步骤如下:
1、创建JProgressBar对象
public JProgressBar(int orient, int min, int max);
orint
:方向min
:最小值max
:最大值2、设置属性
// 设置进度条是否有边框
setBorderPainted(boolean b);
// 设置当前进度条是不是进度不确定的进度条,如果是,则将看到一个滑块在进度条中左右移动
setIndeterminate(boolean newValue);
// 设置进度条是否显示当前完成的百分比
setStringPainted(boolean b);
3、获取和设置当前进度条的进度状态
// 设置当前进度值
setValue(int n);
// 获取进度条的完成百分比
double getPercentComplete();
// 返回进度字符串的当前值
String getStrin();
public class JProgressBarDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JProgressBarDemo");
JProgressBar pb1 = new JProgressBar();
JProgressBar pb2 = new JProgressBar();
pb2.setBorderPainted(false);
JProgressBar pb3 = new JProgressBar();
pb3.setBorderPainted(true);
pb3.setStringPainted(true);
JProgressBar pb4 = new JProgressBar();
pb4.setIndeterminate(true);
Box verticalBox = Box.createVerticalBox();
verticalBox.add(pb1);
verticalBox.add(pb2);
verticalBox.add(pb3);
verticalBox.add(pb4);
jFrame.add(verticalBox);
jFrame.pack();
jFrame.setVisible(true);
// 创建一个线程模拟操作
MyThread myThread = new MyThread();
new Thread(myThread).start();
// 定时器去获取线程的属性值
new Timer(100, e -> {
pb1.setValue(myThread.value);
pb2.setValue(myThread.value);
pb3.setValue(myThread.value);
pb4.setValue(myThread.value);
}).start();
}
static class MyThread implements Runnable {
public volatile int value = 0;
@Override
public void run() {
while (value < 100){
try {
Thread.sleep(100);
value++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
ProgressMonitor
的用法与JProgressBar
的用法基本相似,只是ProgressMonitor
可以直接创建一个进度对话框,它提供了下面的构造器完成对话框的创建:
public ProgressMonitor(Component parentComponent,Object message,String note, int min,int max);
parentComponent
:对话框的父组件message
:对话框的描述信息note
:对话框的提示信息min
:进度条的最小值max
:进度条的最大值public class ProgressMonitorDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("ProgressMonitorDemo");
JButton jButton = new JButton("开始");
jButton.addActionListener(e -> {
ProgressMonitor progressMonitor = new ProgressMonitor(jFrame, "进度条", "正在加载...", 0, 100);
MyTask myTask = new MyTask();
new Thread(myTask).start();
Timer timer = new Timer(100, a -> {
progressMonitor.setProgress(myTask.progress);
});
timer.start();
});
jFrame.add(jButton);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(300,300);
jFrame.setVisible(true);
}
static class MyTask implements Runnable{
private volatile int progress = 0;
@Override
public void run() {
while (progress < 100){
try {
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
progress++;
}
}
}
}
无论从哪个角度来看, JList 和 JComboBox 都是极其相似的,它们都有一个列表框,只是 JComboBox的列表框需要 以下拉方式显示出来; JList 和 JComboBox 都可以通过调用 setRenderer方法来改变列表项的表现形式 。甚至维护这两个组件的 Model 都是相似的, JList 使用 ListModel, JComboBox 使用ComboBoxModel ,而 ComboBoxModel 是 ListModel 的子类 。
使用JList或JComboBox实现简单列表框的步骤如下:
1、创建JList或JComboBox对象
// 创建JList对象,把listData数组中的每项内容转换成一个列表项展示
JList(final E[] listData);
// 创建JList对象,把listData数组中的每项内容转换成一个列表项展示
JList(final Vector<? extends E> listData);
JComboBox(E[] items);
JComboBox(Vector<E> items);
2、设置JList或JComboBox的外观行为
---------------------------JList----------------------------------------------
// 在已经选中列表项的基础上,增加选中从anchor到lead索引范围内的所有列表项
addSelectionInterval(int anchor, int lead);
// 设置列表项的高度和宽度
setFixedCellHeight(int height)/setFixedCellWidth(int width);
// 设置列表框的布局方向
setLayoutOrientation(int layoutOrientation);
// 设置默认选中项
setSelectedIndex(int index);
// 设置默认选中的多个列表项
setSelectedIndices(int[] indices);
// 设置默认选中项,并滚动到该项显示
setSelectedValue(Object anObject,boolean shouldScroll);
// 设置选中项的背景颜色
setSelectionBackground(Color selectionBackground);
// 设置选中项的前景色
setSelectionForeground(Color selectionForeground);
// 设置从anchor到lead范围内的所有列表项被选中
setSelectionInterval(int anchor, int lead);
// 设置选中模式,默认没有限制,也可以设置为单选或者区域选中
setSelectionMode(int selectionMode);
// 设置列表框的可是高度足以显示多少行列表项
setVisibleRowCount(int visibleRowCount);
---------------------------JComboBox----------------------------------------------
// 设置是否可以直接修改列表文本框的值,默认为不可以
setEditable(boolean aFlag);
// 设置列表框的可是高度足以显示多少行列表项
setMaximumRowCount(int count);
// 设置默认选中项
setSelectedIndex(int anIndex);
// 根据列表项的值,设置默认选中项
setSelectedItem(Object anObject);
3、设置监听器,监听列表项的变化,JList通过addListSelectionListener
完成,JComboBox通过addItemListener
完成
前面只是介绍了如何创建 JList 、 JComboBox 对象, 当调用 JList 和 JComboBox构造方法时时传入数组或 Vector 作为参数,这些数组元素或集合元素将会作为列表项。当使用JList 或 JComboBox 时 常常还需要动态地增加、删除列表项,例如JCombox提供了下列方法完成增删操作:
// 添加一个列表项
addItem(E item);
// 向指定索引处插入一个列表项
insertItemAt(E item, int index);
// 删除所有列表项
removeAllItems();
// 删除指定列表项
removeItem(Object anObject);
// 删除指定索引处的列表项
removeItemAt(int anIndex);
JList 并没有提供这些类似的方法。如果需要创建一个可以增加、删除列表项的 JList 对象,则应该在创建 JList 时显式使用 DefaultListModel
作为构造参数 。因为 DefaultListModel
作为 JList 的 Model,它负责维护 JList 组件的所有列表数据,所以可以通过向 DefaultListModel 中添加、删除元素来实现向 JList 对象中增加 、删除列表项 。DefaultListModel
提供了如下几个方法来添加、删除元素:
// 在该 ListModel 的指定位置处插入指定元素 。
add(int index, E element);
// 将指定元素添加到该 ListModel 的末尾 。
addElement(E obj);
// 在该 ListModel 的指定位置处插入指定元素 。
insertElementAt(E obj, int index);
// 删除该 ListModel 中指定位置处的元素
Object remove(int index);
// 删除该 ListModel 中的所有元素,并将其的大小设置为零 。
removeAllElements();
// 删除该 ListModel 中第一个与参数匹配的元素。
removeElement(E obj);
// 删除该 ListModel 中指定索引处的元素 。
removeElementAt(int index);
// 删除该 ListModel 中指定范围内的所有元素。
removeRange(int 企omIndex , int toIndex);
// 将该 ListModel 指定索引处的元素替换成指定元素。
set(int index, E element);
// 将该 ListModel 指定索引处的元素替换成指定元素。
setElementAt(E obj, int index);
前面程序中的 JList 和 JComboBox 采用的都是简单的字符串列表项, 实际上 , JList 和 JComboBox还可以支持图标列表项,如果在创建 JList 或 JComboBox 时传入图标数组,则创建的 JList 和 JComboBox的列表项就是图标 。
public interface ListCellRenderer<E> {
Component getListCellRendererComponent(
JList<? extends E> list,//列表组件
E value,//当前列表项的值额索引
int index,//当前列表项d
boolean isSelected,//当前列表项是否被选中
boolean cellHasFocus);//当前列表项是否获取了焦点
}
通过JList
的setCellRenderer(ListCellRenderer<? super E> cellRenderer)
方法,把自定义的ListCellRenderer
对象传递给JList
,就可以按照自定义的规则绘制列表项组件了。
Swing 使用 JTree 对象来代表一棵树,JTree 树中节点可以使用 TreePath 来标识,该对象封装了当前节点及其所有的父节点。
当一个节点具有子节点时,该节点有两种状态:
JTree常用构造方法:
// 使用指定 的数据模型创建 JTree 对象,它默认显示根节点。
JTree(TreeModel newModel);
// 使用 root 作为根节点创建 JTree 对象,它默认显示根节点 。
JTree(TreeNode root);
//使用root作为根节点创建JTree对象,它默认显示根节点。 asksAllowsChildren 参数控制怎样的节点才算叶子节点,如果该参数为 true ,则只有当程序使用 setAllowsChildren(false)显式设置某个节点不允许添加子节点时(以后也不会拥有子节点) ,该节点才会被 JTree 当成叶子节点:如果该参数为 false ,则只要某个节点当时没有子节点(不管以后是否拥有子节点) ,该节点都会被 JTree 当成叶子节点。
JTree(TreeNode root, boolean asksAllowsChildren);
// 设置结点之间没有连接线
tree.putClientProperty( "JTree.lineStyle", "None");
// 设置结点之间只有水平分割线
tree.putClientProperty("JTree.lineStyle" , "Horizontal");
// 设置根结点有"展开、折叠"图标
tree.setShowsRootHandles(true);
// 隐藏根结点
tree.setRootVisible(false);
// 按广度优先的顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象 。
Enumeration breadthFirstEnumeration/preorderEnumeration();
// 按深度优先的顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象 。
Enumeration depthFirstEnumeration/postorderEnumeration();
// 返回此结点的下一个兄弟结点 。
DefaultMutableTreeNode getNextSibling();
// 返回此结点的父结点。如果此结点没有父结点,则返回null 。
TreeNode getParent();
// 返回从根结点到达此结点的所有结点组成的数组。
TreeNode[] getPath();
// 返回此结点的上一个兄弟结点。
DefaultMutableTreeNode getPreviousSibling();
// 返回包含此结点的树的根结点。
TreeNode getRoot();
// 返回此结点和aNode最近的共同祖先。
TreeNode getSharedAncestor(DefaultMutableTreeNode aNode);
// 返回此结点的兄弟结点数。
int getSiblingCount();
// 返回该结点是否是叶子结点 。
boolean isLeaf()
// 判断anotherNode是否是当前结点的祖先结点(包括父结点) 。
boolean isNodeAncestor(TreeNode anotherNode);
// 如果aNode是此结点的子结点,则返回true。
boolean isNodeChild(TreeNode aNode);
// 如果 anotherNode 是此结点的后代,包括是此结点本身、此结点的子结点或此结点的子结点的后代,都将返回true 。
boolean isNodeDescendant(DefaultMutableTreeNode anotherNode);
// 当aNode和当前结点位于同一棵树中时返回 true 。
boolean isNodeRelated(DefaultMutableTreeNode aNode);
// 返回anotherNode是否是当前结点的兄弟结点 。
boolean isNodeSibling(TreeNode anotherNode);
// 返回当前结点是否是根结点 。
boolean isRoot();
// 返回从指定祖先结点到当前结点的所有结点组成的枚举对象 。
Enumeration pathFromAncestorEnumeration(TreeNode ancestor);
在构建目录树时,可以先创建很多DefaultMutableTreeNode
对象,并调用他们的add方法构建好子父级结构,最后根据根节点构建一个JTree即可。
public class JTreeDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JTreeDemo");
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode shanxi = new DefaultMutableTreeNode("陕西");
DefaultMutableTreeNode henan = new DefaultMutableTreeNode("河南");
DefaultMutableTreeNode xian = new DefaultMutableTreeNode("西安");
DefaultMutableTreeNode weinan = new DefaultMutableTreeNode("渭南");
DefaultMutableTreeNode zhengzhou = new DefaultMutableTreeNode("郑州");
DefaultMutableTreeNode luoyang = new DefaultMutableTreeNode("洛阳");
shanxi.add(xian);
shanxi.add(weinan);
henan.add(zhengzhou);
henan.add(luoyang);
root.add(shanxi);
root.add(henan);
JTree jTree = new JTree(root);
jFrame.add(jTree);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(300,300);
jFrame.setVisible(true);
}
}
JTree 生成的树默认是不可编辑的,不可以添加、删除结点,也不可以改变结点数据。
如果想让某个 JTree 对象变成可编辑状态,则可以调用 JTree 的setEditable(boolean b)
方法,传入 true
即可把这棵树变成可编辑的树(可以添加、删除结点,也可以改变结点数据)。
编辑树结点的步骤如下:
1、获取被选中节点,有两种方式如下:
方式一:通过JTree对象的某些方法,例如 TreePath getSelectionPath()
等,得到一个TreePath对象,包含了从根结点到当前结点路径上的所有结点;然后再调用TreePath对象的 Object getLastPathComponent()
方法,得到当前选中结点;
方式二:调用JTree对象的 Object getLastSelectedPathComponent()
方法获取当前被选中的结点;
2、调用DefaultTreeModel
数据模型有关增删改的一系列方法完成编辑,方法执行完后,会自动重绘JTree
public class JTreeDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JTreeDemo");
DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
DefaultMutableTreeNode shanxi = new DefaultMutableTreeNode("陕西");
DefaultMutableTreeNode henan = new DefaultMutableTreeNode("河南");
DefaultMutableTreeNode xian = new DefaultMutableTreeNode("西安");
DefaultMutableTreeNode weinan = new DefaultMutableTreeNode("渭南");
DefaultMutableTreeNode zhengzhou = new DefaultMutableTreeNode("郑州");
DefaultMutableTreeNode luoyang = new DefaultMutableTreeNode("洛阳");
shanxi.add(xian);
shanxi.add(weinan);
henan.add(zhengzhou);
henan.add(luoyang);
root.add(shanxi);
root.add(henan);
JTree jTree = new JTree(root);
// 打开树的编辑功能
jTree.setEditable(true);
// 监听鼠标拖动,完成树的拖动
jTree.addMouseListener(new MouseAdapter() {
private TreePath tp;
// 鼠标按下事件
@Override
public void mousePressed(MouseEvent e) {
// 获取当前鼠标所在的节点
tp = jTree.getPathForLocation(e.getX(), e.getY());
}
// 鼠标释放事件
@Override
public void mouseReleased(MouseEvent e) {
TreePath targetPath = jTree.getPathForLocation(e.getX(), e.getY());
if (tp != null && targetPath != null) {
// 禁止拖动到子节点,和自身节点
// isDescendant:判断targetPath是否为tp的子节点
if (targetPath.isDescendant(tp) || tp.equals(targetPath)){
System.out.println("不能拖动到子节点或自身节点!");
return;
}
System.out.println("拖动成功");
DefaultMutableTreeNode parent = (DefaultMutableTreeNode) targetPath.getLastPathComponent();
DefaultMutableTreeNode child = (DefaultMutableTreeNode) tp.getLastPathComponent();
// add方法内部,先将该结点从原父结点删除,然后再把该结点添加到新结点中
parent.add(child);
tp = null;
jTree.updateUI();
}
}
});
jFrame.add(jTree);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(300,300);
jFrame.setVisible(true);
}
}
使用JTable创建简单表格步骤:
public class JTableDemo {
public static void main(String[] args) {
JFrame jFrame = new JFrame("JTableDemo");
Object[] titles = {"编号", "姓名", "性别", "年龄"};
Object[][] data = {
{1, "张三", "男", 18},
{2, "李四", "女", 19},
{3, "王五", "男", 20},
{4, "赵六", "女", 21},
{5, "孙七", "男", 22},
{6, "周八", "女", 23},
{7, "吴九", "男", 24},
{8, "郑十", "女", 25},
};
JTable jTable = new JTable(data, titles);
jFrame.add(new JScrollPane(jTable));
jFrame.pack();
jFrame.setVisible(true);
}
}
在上面实现的简单表格中,大概有如下几个功能:
JTable提供了一个setAutoResizeMode(int mode)
方法用来调整表格的格式,该方法可以接收下面几个值:
JTable.AUTO_RESIZE_OFF
:关闭表格的自动调整功能。当调整某一列的宽度时,其他列的宽度不会发生变化;JTable.AUTO_RESIZE_NEXT_COLUMN
:只调整下一列的宽度,其他列及表格的宽度不会发生改变;JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
:平均调整当前列后面所有列的宽度,当前列的前面所有列及表格的宽度都不会发生变化,这是默认的调整方式JTable.AUTO_RESIZE_LAST_COLUMN
:只调整最后一列的宽度,其他列及表格的宽度不会发生变化;JTable.AUTO_RESIZE_ALL_COLUMNS
:平均调整表格中所有列的宽度,表格的宽度不会发生改变如果需要精确控制每一列的宽度,则可通过 TableColumn
对象来实现 。 JTable 使用 TableColumn 来表示表格中的每一列, JTable 中表格列的所有属性,如最佳宽度、是否可调整宽度、最小和最大宽度等都保存在该 TableColumn 中 。 此外, TableColumn 还允许为该列指定特定的单元格绘制器和单元格编辑器(这些内容将在后面讲解) 。 TableColumn 具有如下方法 。
setMaxWidth(int maxWidth)
:设置该列的最大宽度 。 如果指定的 maxWidth 小于该列的最小宽度, 则 maxWidth 被设置成最小宽度 。setMinWidth(int minWidth)
:设置该列的最小宽度 。setPreferredWidth(int preferredWidth)
:设置该列的最佳宽度 。setResizable(boolean isResizable)
:设置是否可以调整该列的宽度 。sizeWidthToFit()
:调整该列的宽度,以适合其标题单元格的宽度 。setRowSelectionAllowed(boolean rowSelectionAllowed)
来修改;setColumnSelectionAllowed(boolean columnSelectionAllowed)
方法,修改当前JTable的选择模式为列;setCellSelectionEnabled(boolean cellSelectionEnabled)
,修改当前JTable的选择模式为单元格;与 JList 、JTree 类似的是, JTable 使用了 一个ListSelectionModel 表示该表格的选择状态,程序可以通过 ListSelectionModel 来控制 JTable 的选择模式 。 JTable 的选择模式有如下三种:
ListSelectionMode.MULTIPLE_INTERVAL_SELECTION
:没有任何限制,可以选择表格中任何表格单元,这是默认的选择模式。通过Shift 和 Ctrl 辅助键的帮助可以选择多个表格单元 。ListSelectionMode.SINGLE_INTERVAL_SELECTION
:选择单个连续区域,该选项可以选择多个表格单元,但多个表格单元之间必须是连续的。通过 Shift 辅助键的帮助来选择连续区域。ListSelectionMode.SINGLE_SELECTION
:只能选择单个表格单元 。public class AdjustingWidthDemo {
JFrame jf = new JFrame("调整表格宽度");
JMenuBar menuBar = new JMenuBar();
JMenu adjustModeMenu = new JMenu("调整方式");
JMenu selectUnitMenu = new JMenu("选择单元");
JMenu selectModeMenu = new JMenu("选择方式");
//定义5个单选框按钮,用以控制表格的宽度调整方式
JRadioButtonMenuItem[] adjustModeItem = new JRadioButtonMenuItem[5];
//定义3个单选框按钮,用以控制表格的选择方式
JRadioButtonMenuItem[] selectModeItem = new JRadioButtonMenuItem[3];
//定义复选菜单项,控制选择单元
JCheckBoxMenuItem rowsItem = new JCheckBoxMenuItem("选择行");
JCheckBoxMenuItem columnItem = new JCheckBoxMenuItem("选择列");
JCheckBoxMenuItem cellItem = new JCheckBoxMenuItem("选择单元格");
//定义按钮组,实现单选
ButtonGroup adjustBg = new ButtonGroup();
ButtonGroup selectBg = new ButtonGroup();
//定义一个int类型的数组,用于保存表格所有的宽度调整方式
int[] adjustModes = {
JTable.AUTO_RESIZE_OFF,
JTable.AUTO_RESIZE_NEXT_COLUMN,
JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
JTable.AUTO_RESIZE_LAST_COLUMN,
JTable.AUTO_RESIZE_ALL_COLUMNS
};
//定义一个int乐行数组,用于保存表格所有的选择方式
int[] selectModes = {
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION,
ListSelectionModel.SINGLE_INTERVAL_SELECTION,
ListSelectionModel.SINGLE_SELECTION
};
//声明JTable
JTable table;
//定义一个二维数组,作为表格行数据
Object[][] tableData = {
new Object[]{"李清照",29,"女"},
new Object[]{"苏格拉底",56,"男"},
new Object[]{"李白",35,"男"},
new Object[]{"弄玉",18,"女"},
new Object[]{"虎头",2,"男"},
};
//定义一个一维数组,作为列标题
Object[] columnTitle = {"姓名","年龄","性别"};
public void init(){
//创建JTable对象
table = new JTable(tableData,columnTitle);
//-----------------为窗口安装设置表格调整方式的菜单--------------------
adjustModeItem[0] = new JRadioButtonMenuItem("只调整表格");
adjustModeItem[1] = new JRadioButtonMenuItem("只调整下一列");
adjustModeItem[2] = new JRadioButtonMenuItem("平均调整余下列");
adjustModeItem[3] = new JRadioButtonMenuItem("只调整最后一列");
adjustModeItem[4] = new JRadioButtonMenuItem("平均调整所有列");
menuBar.add(adjustModeMenu);
for (int i = 0; i < adjustModeItem.length; i++) {
//默认选中第三个菜单项,即对应表格默认的宽度调整方式
if (i==2){
adjustModeItem[i].setSelected(true);
}
adjustBg.add(adjustModeItem[i]);
adjustModeMenu.add(adjustModeItem[i]);
//为菜单项设置事件监听器
int index = i;
adjustModeItem[i].addActionListener(e -> {
if (adjustModeItem[index].isSelected()){
table.setAutoResizeMode(adjustModes[index]);
}
});
}
//---------------为窗口安装设置表格选择方式的菜单-------------------
selectModeItem[0] = new JRadioButtonMenuItem("无限制");
selectModeItem[1] = new JRadioButtonMenuItem("单独的连续区");
selectModeItem[2] = new JRadioButtonMenuItem("单选");
menuBar.add(selectModeMenu);
for (int i = 0; i < selectModeItem.length; i++) {
//默认选中第一个菜单项,即表格的默认选择方式
if (i==0){
selectModeItem[i].setSelected(true);
}
selectBg.add(selectModeItem[i]);
selectModeMenu.add(selectModeItem[i]);
int index = i;
selectModeItem[i].addActionListener(e -> {
if (selectModeItem[index].isSelected()){
table.getSelectionModel().setSelectionMode(selectModes[index]);
}
});
}
//---------------为窗口添加选择单元菜单----------------------
menuBar.add(selectUnitMenu);
rowsItem.setSelected(table.getRowSelectionAllowed());
columnItem.setSelected(table.getColumnSelectionAllowed());
cellItem.setSelected(table.getCellSelectionEnabled());
rowsItem.addActionListener(e -> {
//清除表格的选中状态
table.clearSelection();
//如果该菜单项处于选中状态,设置表格的选择单元是行
table.setRowSelectionAllowed(rowsItem.isSelected());
//如果选择行、选择列同时被选中,其实质是选择单元格
table.setCellSelectionEnabled(table.getCellSelectionEnabled());
});
selectUnitMenu.add(rowsItem);
columnItem.addActionListener(e -> {
//清除表格的选中状态
table.clearSelection();
//如果该菜单项处于选中状态,设置表格的选择单元是列
table.setColumnSelectionAllowed(columnItem.isSelected());
///如果选择行、选择列同时被选中,其实质是选择单元格
table.setCellSelectionEnabled(table.getCellSelectionEnabled());
});
selectUnitMenu.add(columnItem);
cellItem.addActionListener(e -> {
//清除表格的选中状态
table.clearSelection();
//如果该菜单项处于选中状态,设置表格的选择单元是单元格
table.setCellSelectionEnabled(cellItem.isSelected());
///该选项的改变会同时影响选择行、选择列两个菜单
table.setRowSelectionAllowed(table.getRowSelectionAllowed());
table.setColumnSelectionAllowed(table.getColumnSelectionAllowed());
});
selectUnitMenu.add(cellItem);
jf.setJMenuBar(menuBar);
//分别获取表格的三个表格列,并设置三列的最小宽、最佳宽度和最大宽度
TableColumn nameColumn = table.getColumn(columnTitle[0]);
nameColumn.setMinWidth(40);
TableColumn ageColumn = table.getColumn(columnTitle[1]);
ageColumn.setPreferredWidth(50);
TableColumn genderColumn = table.getColumn(columnTitle[2]);
genderColumn.setMaxWidth(50);
jf.add(new JScrollPane(table));
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void main(String[] args) {
new AdjustingWidthDemo().init();
}
}
与 JList、 JTree 类似的是 , JTable 采用了 TableModel 来保存表格中的所有状态数据 : 与 ListModel类似的是, TableModel 也不强制保存该表格显示的数据 。 虽然在前面程序中看到的是直接利用一个二维数组来创建 JTable 对象,但也可以通过 TableModel 对象来创建表格。
使用TableModel步骤如下:
1、自定义类,继承AbstractTableModel
抽象类,重写下面几个方法:
// 返回表格列的数量
int getColumnCount();
// 返回表格行的数量
int getRowCount();
// 返回rowIndex行,column列的单元格的值
Object getValueAt(int rowIndex, int columnIndex);
// 返回columnIndex列的列名称
String getColumnName(int columnIndex);
// 设置rowIndex行,columnIndex列单元格是否可编辑
boolean isCellEditable(int rowIndex, int columnIndex);
// 设置rowIndex行,columnIndex列单元格的值
setValueAt(Object aValue, int rowIndex, int columnIndex);