窗体上的按钮、标签、文本框等都被称为“窗体组件”,简称“组件”。大部分组件都是Jcomponent类的子类,而Jcomponent又是Container的子类、Container又是Component的子类。这3个类中所定义的方法都会都会被组件各种组件继承下来,因此大部分组件都有相同的方法,这些方法如表20-5所示。
表20-5 Jcomponent常用方法
本小节将详细讲解各种Swing组件的使用方法。
20.5.1标签组件JLabel
在Swing体系中用Jlabel类表示标签组件。标签的作用是向窗体上贴一段文字或一副图片,JLabel可以通过setText()方法设置文字,也可以通过getText()方法获取标签上的文字。也可以通过继承而来的方法设置字体、图片、鼠标形状等各种外观属性。下面的【例20_08】展示了JLabel的使用方法。
【例20_08 JLabel的使用】
Exam20_08.java
import javax.swing.*;
import java.awt.*;
class Exam20_08Frame extends JFrame {
JLabel label1;
JLabel label2;
public Exam20_08Frame( ){
init();
private void init( ){
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
label1 = new JLabel("你好");
label1.setSize(200, 100);
label1.setLocation(0, 0);
//设置label1上的字体为斜体隶书,大小为40磅
label1.setFont(new Font("隶书", Font.ITALIC, 40));
label2 = new JLabel();
label2.setSize(100, 100);
label2.setLocation(220, 0);
label2.setIcon(new ImageIcon("logo.png"));
//设置label2的鼠标形状为手形
label2.setCursor(new Cursor(Cursor.HAND_CURSOR));
container.add(label1);
container.add(label2);
public class Exam20_08 {
public static void main(String[] args) {
Exam20_08Frame frame = new Exam20_08Frame();
frame.setSize(400, 200);//设置窗体的大小
frame.setLocationRelativeTo(null);//设置窗体出现在屏幕正中间
frame.setTitle("Exam20_08Frame");//设置窗体的标题
//设置关闭窗体时同时停止程序
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);//设置窗体可见
【例20_08】的窗体上添加了两个标签分别是label1和label2,程序中设置了label1的字体为斜体隶书,大小为40磅,而为label2设置了一个图片logo.png。需要注意,读者只有在项目文件夹的根目录下添加一个logo.png文件才能使这个图片正确显示出来。【例20_08】的运行结果如图20-7所示。
图20-7【例20_08】运行结果
读者把鼠标放在label2上时,会看到鼠标变成了手形。
20.5.2文本框JTextField和密码框JPasswordField
在Swing体系中用JTextField类来表示文本框,而用JPasswordField类表示密码框。文本框是用来输入单行文本的,而密码框是用来输入密码的。文本框和密码框的方法大部分都是继承而来的,在表20-5中基本上都能找到,而JPasswordField新增了一个getPassword()来获得密码框中的内容,这个方法返回一个char型数组,当获得这个数组后可以以它为构造方法的参数创建一个String类对象。此外,程序员如果想设置密码框中显示的字符,可以调用JPasswordField类的setEchoChar()方法实现,这个方法的参数是char型,表示显示在密码框中的字符。下面的【例20_09】展示了文本框和密码框的使用方法。
【例20_09 JTextField和JPasswordField的使用】
Exam20_09.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class Exam20_09Frame extends JFrame{
JLabel lblUserName;
JLabel lblPassword;
JTextField txtUserName;
JPasswordField pwdPassword;
JButton btnLogin;
JButton btnCancel;
public Exam20_09Frame(){
init();
private void init( ){
Container container = this.getContentPane();
container.setLayout(null);
lblUserName = new JLabel("用户名");
lblPassword = new JLabel("密 码");
txtUserName = new JTextField();
pwdPassword = new JPasswordField();
btnLogin = new JButton("登录");
btnCancel = new JButton("取消");
lblUserName.setSize(70, 40);
lblUserName.setLocation(100, 60);
lblUserName.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lblPassword.setSize(70, 40);
lblPassword.setLocation(100, 160);
lblPassword.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txtUserName.setSize(300, 40);
txtUserName.setLocation(200, 60);
pwdPassword.setSize(300, 40);
pwdPassword.setLocation(200, 160);
//设置密码框中显示的字符为*
pwdPassword.setEchoChar('*');
btnLogin.setSize(150, 40);
btnLogin.setLocation(130, 240);
btnLogin.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String userName = txtUserName.getText();
String password = new String(pwdPassword.getPassword());
System.out.println("用户名: "+userName+" 密码:"+password);
System.out.println("登录中...");
btnCancel.setSize(150, 40);
btnCancel.setLocation(320, 240);
btnCancel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
container.add(lblUserName);
container.add(lblPassword);
container.add(txtUserName);
container.add(pwdPassword);
container.add(btnLogin);
container.add(btnCancel);
public class Exam20_09 {
public static void main(String[] args) {
Exam20_09Frame frame = new Exam20_09Frame();
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_09Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_09】中,窗体上有一个文本框和一个密码框,并设置密码框中显示的字符是“*”,【例20_09】的运行结果如图20-8所示。
图20-8【例20_09】运行结果
单击窗体上的“登录”按钮,可以看到控制台上输出了当前填写的用户名和密码。
20.5.3文本区JTextArea
在Swing体系中JTextArea类来表示文本区。所谓文本区就是多行文本框,它是用来输入大段文本信息的组件。与JTextField 不同,JTextArea不支持添加ActionListener监听器。用户在单行文本框中按下回车键,能够触发ActionListener监听器处理事件,而在文本区中按下回车键,只能起到文本手动换行的作用。文本区组件相比较文本框组件而言,增加了大量用来编辑文字的方法,如表20-6所示。
表20-6 JTextArea编辑文字的方法
Swing体系不支持“撤销”和“重做”的快捷键,希望实现用快捷键的方式完成在“撤销”和“重做”操作,必须借助UndoManager类,这个类本质上其实是一个监听器类,程序员可以创建它的一个对象,让这个对象监听某个文本区上所做的操作。当某个文本区添加了这种类型的监听器之后,用户对这个文本区输入了哪些文本,输入的顺序是怎样的,在输入过程中是否做过撤销和重做的操作,监听器都会了如指掌,并且它还会把这些操作步骤全部都记录下来。如果希望实现撤销操作,需要先调用UndoManager对象的canUndo()方法来判断一下能否做撤销操作,如果能做撤销操作,就调用undo()方法就能实现撤销。实现重做的原理也是一样的,通过canRedo()方法实现判断是否能够重做,如果能重做,那么调用redo()方法实现重做。需要注意:判断是否能够撤销和重做都是需要程序员写代码手动调用相应判断方法,监听器对象本身并不会自动判断。
给文本区设置快捷键的基本原理也很简单,只需要在文本区上添加一个KeyListener(键盘事件监听器)。每次按下一个键的时候,都在相应的事件处理方法中判断一下是不是程序员所设定的快捷键。KeyListener所有处理键盘事件的方法都有一个KeyEvent类型的参数,这个参数对象是由Java运行时系统在捕捉到事件之后创建并传递给处理事件方法的。在这个参数对象当中包含了很多信息,其中最主要的信息就是用户所按下的是哪一个键,以及是否按下了Ctrl键。假如键盘事件对象是e,则判断是否按下了Ctrl+A的代码是:
e.isControlDown() && e.getKeyCode()==KeyEvent.VK_Z
而由于文本区自身又并不自带滚动条,这就导致当文本区中的文字越来越多时,下面的文字无法显示出来。解决这个问题的办法是把文本区添加到JScrollPane中。JScrollPane是滚动面板,它是一个容器组件,这个组件会自带滚动条。此外,窗体上的文本区组件常常要通过setLineWrap(true)设置允许自动换行,如果不做这个设置,文本区中的文本无法实现自动换行。下面的【例20_10】展示了文本区组件的操作方式,在这个例子中,窗体上有两个文本区,用户可以把左边文本框中的文本赋值到剪切板,然后插入或添加到右边的文本区中。
【例20_10 JTextArea的使用】
Exam20_10.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
class Exam20_10Frame extends JFrame{
JTextArea txaSrc;
JTextArea txaDest;
JButton btnAppend;
JButton btnInsert;
JButton btnSelectAll;
JButton btnCopy;
JButton btnPaste;
JButton btnUndo;
JButton btnRedo;
UndoManager undoManager;
JScrollPane jspSrc;
public Exam20_10Frame(){
init();
private void init( ){
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
undoManager = new UndoManager();
txaSrc = new JTextArea();
txaSrc.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txaSrc.setLineWrap(true);
txaSrc.getDocument().addUndoableEditListener(undoManager);
txaSrc.addKeyListener(new KeyAdapter(){
@Override
public void keyPressed(KeyEvent e) {
//实现撤销操作的快捷键
if(e.isControlDown() && e.getKeyCode()==KeyEvent.VK_Z){
if(undoManager.canUndo()){
undoManager.undo();
//实现重做操作的快捷键
if(e.isControlDown() && e.getKeyCode()==KeyEvent.VK_Y){
if(undoManager.canRedo()){
undoManager.redo();
jspSrc = new JScrollPane(txaSrc);
jspSrc.setSize(300, 150);
jspSrc.setLocation(80, 0);
txaDest = new JTextArea();
txaDest.setSize(300, 150);
txaDest.setLocation(420, 0);
txaDest.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txaDest.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
btnAppend = new JButton("添加");
btnAppend.setSize(80, 40);
btnAppend.setLocation(60, 200);
btnAppend.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnAppend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txaSrc.append("ab");
btnInsert = new JButton("插入");
btnInsert.setSize(80, 40);
btnInsert.setLocation(160, 200);
btnInsert.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnInsert.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txaSrc.insert("c", 0);
btnSelectAll = new JButton("全选");
btnSelectAll.setSize(80, 40);
btnSelectAll.setLocation(260, 200);
btnSelectAll.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnSelectAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txaSrc.requestFocus();
txaSrc.selectAll();
btnCopy = new JButton("复制");
btnCopy.setSize(80, 40);
btnCopy.setLocation(360, 200);
btnCopy.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnCopy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txaSrc.copy();
btnPaste = new JButton("粘贴");
btnPaste.setSize(80, 40);
btnPaste.setLocation(460, 200);
btnPaste.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnPaste.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
txaDest.paste();
btnUndo = new JButton("撤销");
btnUndo.setSize(80, 40);
btnUndo.setLocation(560, 200);
btnUndo.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnUndo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(undoManager.canUndo()){
undoManager.undo();
btnRedo = new JButton("重做");
btnRedo.setSize(80, 40);
btnRedo.setLocation(660, 200);
btnRedo.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnRedo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(undoManager.canRedo()){
undoManager.redo();
container.add(jspSrc);
container.add(txaDest);
container.add(btnAppend);
container.add(btnInsert);
container.add(btnCopy);
container.add(btnPaste);
container.add(btnUndo);
container.add(btnSelectAll);
container.add(btnRedo);
public class Exam20_10 {
public static void main(String[] args) {
Exam20_10Frame frame = new Exam20_10Frame();
frame.setSize(800, 300);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_10Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_10】的运行结果如图20-9所示。
图20-9【例20_10】运行结果
20.5.4复选框JCheckBox
Swing体系用JCheckBox类来表示复选框,JCheckBox和表示按钮的JButton都是AbstractButton的子类,因此复选框也可以被看作是一种特殊的按钮。复选框能够实现多项选择,在某些场景下也可以用于让用户完成实现确认,在这种情况下复选框也被称为“检查框”,如图20-10所示。
图20-10用户确认检查框
窗体上如果出现复选框,程序员可以通过AbstractButton类中定义的isSelected()方法来判断某个复选框是否被选中,由于JCheckBox是AbstractButton的子类,所以JCheckBox也具有这个方法。如果希望获得复选框的文本,则需要调用getText()方法实现。
JCheckBox除了可以添加ActionListener作为监听器外,还可以添加ItemListener作为监听器,ItemListener用于监听选项的变化,如果一个复选框被选中或取消选中都会出发该监听器的处理方法,ItemListener监听器的处理方法是itemStateChanged(),从方法的名称不难看出它表示选项状态的变化。下面的【例20_11】展示了如何使用检查框完成用户确认以及使用复选框完成多选。
【例20_11 JCheckBox的使用】
Exam20_11.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Exam20_11Frame extends JFrame{
JCheckBox cbxConfirm;
JButton btnNext;
String[] names;
JCheckBox[] cbxCities;
JButton btnOK;
JLabel lblCities;
public Exam20_11Frame() {
init();
private void init() {
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
cbxConfirm = new JCheckBox("我已阅读并接受条款");
btnNext = new JButton("下一步");
lblCities = new JLabel("选择你去过的城市");
names = new String[]{"北京","上海","广州","深圳"};
cbxCities = new JCheckBox[names.length];
btnOK = new JButton("提交");
cbxConfirm.setSize(250, 40);
cbxConfirm.setLocation(30, 30);
cbxConfirm.setFont(new Font("微软雅黑", Font.PLAIN, 20));
cbxConfirm.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
boolean flag = cbxConfirm.isSelected();
btnNext.setEnabled(flag);
btnNext.setSize(100, 40);
btnNext.setLocation(30, 80);
btnNext.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnNext.setEnabled(false);
lblCities.setSize(200, 40);
lblCities.setLocation(30, 150);
lblCities.setFont(new Font("微软雅黑", Font.PLAIN, 20));
for(int i=0;i<cbxCities.length;i++){
cbxCities[i] = new JCheckBox(names[i]);
cbxCities[i].setSize(70, 40);
cbxCities[i].setLocation(30+i*70, 200);
cbxCities[i].setFont(new Font("微软雅黑", Font.PLAIN, 18));
container.add(cbxCities[i]);
btnOK.setSize(80, 40);
btnOK.setLocation(30, 250);
btnOK.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnOK.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String str = "";
for(int i=0;i<cbxCities.length;i++){
if(cbxCities[i].isSelected()==true){
str = str+cbxCities[i].getText()+" ";
System.out.println(str);
container.add(cbxConfirm);
container.add(btnNext);
container.add(lblCities);
container.add(btnOK);
public class Exam20_11 {
public static void main(String[] args) {
Exam20_11Frame frame = new Exam20_11Frame();
frame.setSize(450, 350);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_11Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_11】的运行结果20-11所示。
图20-11【例20_11】运行结果
在图20-11所示窗体中选择“我已阅读并接受条款”,“下一步”按钮就会变成可用状态,而选择几个去过的城市后再单击“提交”按钮,控制台上就会输出所选择的选项。
20.5.5单选按钮JRadioButton
单选按钮也是桌面应用程序中常见到的组件,在Swing体系中用JRadioButton表示单选按钮。单选按钮和复选框有相似之处,它们都是用来让用户完成选择操作的,只不过复选框是用来让用户做多项选择的,而单选按钮是用来让用户做单项选择,最典型的例子是常常用单选按钮让用户选择性别。以单选按钮表示的一组选项必须实现“互斥”效果。所谓互斥就是指从多个选项中只能选择一个,例如从“男”和“女”中只能选中一个选项。为了让一组单选按钮之间能够有互斥效果,必须使用ButtonGroup对象,ButtonGroup相当于一个容器,程序员只需要把单选按钮都放在这个容器中就能实现单选按钮的互斥效果。需要强调:把一组单选按钮加入到ButtonGroup中时,这一组单选按钮必须都已经完成了实例化,否则单选按钮无法完成互斥。
同JCheckBox一样,程序员可以通过isSelected()方法来判断某个单选按钮是否被选中,并通过getText()方法获得某个单选按钮的文本。但通常情况下,一般都只获得被选中的单选按钮的文本,这种情况下可以先调用每一个单选按钮执行setActionCommand()方法对其设置一个“命令文本”,然后调用ButtonGroup的getSelection()获得被选中的选项,最后再调用该选项的getActionCommand()方法获得它的“命令文本”。下面的【例20_12】展示了单选按钮的使用方法及互斥效果。
【例20_12 JRadioButton的使用】
Exam20_12.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Exam20_12Frame extends JFrame{
JButton btnOK;
JLabel lblSex;
JRadioButton rbtMale;
JRadioButton rbtFemale;
ButtonGroup btgSex;
public Exam20_12Frame() {
init();
private void init() {
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
lblSex = new JLabel("请选择性别");
rbtMale = new JRadioButton("男",true);//参数true表示默认选中
rbtFemale = new JRadioButton("女",false);
btnOK = new JButton("确定");
btgSex = new ButtonGroup();
lblSex.setSize(200,40);
lblSex.setLocation(30, 30);
lblSex.setFont(new Font("微软雅黑", Font.PLAIN, 20));
rbtMale.setSize(70,40);
rbtMale.setLocation(30, 70);
rbtMale.setFont(new Font("微软雅黑", Font.PLAIN, 18));
rbtMale.setActionCommand("男");
rbtFemale.setSize(70,40);
rbtFemale.setLocation(100, 70);
rbtFemale.setFont(new Font("微软雅黑", Font.PLAIN, 18));
rbtFemale.setActionCommand("女");
btnOK.setSize(80, 40);
btnOK.setLocation(30, 130);
btnOK.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnOK.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("你选择了:"+btgSex.getSelection().getActionCommand());
container.add(lblSex);
container.add(btnOK);
container.add(rbtMale);
container.add(rbtFemale);
btgSex.add(rbtMale);
btgSex.add(rbtFemale);
public class Exam20_12 {
public static void main(String[] args) {
Exam20_12Frame frame = new Exam20_12Frame();
frame.setSize(250, 250);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_12Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_12】的运行结果20-12所示。
图20-12【例20_12】运行结果
在图20-12所示窗体上选择“男”后,再单击“确定”按钮,控制台上就会输出“你选择了:男”。
20.5.6进度条JProgressBar
在Swing体系中用JProgressBar类来表示进度条。进度条组件用于动态的展示某个任务的完成程度。进度条有一个边框,在边框的中有一块深蓝色的区域用于表示进度。为方便表述,此处把进度条组件中深蓝色的区域称为“蓝条”。
进度条有两种模式,一种叫做“确定模式”,另一种叫做“不确定模式”。此外,进度条还可以被设置为两种方向,一种是是水平方向的进度条,另一种是垂直方向的进度条。此处首先讲解确定模式下水平方向进度条,因为这种进度条在实际开发当中的使用率非常高,超过了90%。确定模式下水平方向的进度条,就如同一把横向摆放的尺子,默认情况下这把尺子左右两端的刻度值分别是0和100。至于数字0和100是什么单位,读者可以不用去管他,因为进度条并不是用来衡量长度的,它只是表示任务的完成程度,所以这把尺子根本不需要有长度单位,只需要有数字就可以了。实际上程序员其实是可以人为的设置进度条两端的刻度值。进度条最左端的刻度值叫做“最小值”,相应的,进度条最右端的刻度值叫做“最大值”。
在确定模式下的水平进度条,蓝条的最左端永远与进度条的最左端是对齐的,因此只需要讨论蓝条右端的位置就可以了。蓝条右端所在的位置称为“当前值”。如果进度条的最小值是0,最大值是100,那么用数字50表示当前值时,蓝条的最右端恰好在整个进度条的正中间。程序员可以通过setValue()方法来设置进度条的当前值。需要注意:进度条不会接受超过范围的数字作为当前值,也就是说不能把当前值设置为比最大值还大或者是比最小值还小的数字。
进度条也可以添加监听器,通常情况下,会给进度条添加ChangeListener,这个监听器会在进度条发生变化的情况下执行事件处理方法。ChangeListener监听器的事件处理方法是stateChanged()。程序员如果希望处理进度条进度变化的事件,就可以把代码写到这个方法中。
下面的【例20_13】展示了确定模式下水平进度条的使用方法。
【例20_13 JprogressBar的使用1】
Exam20_13.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
class Exam20_13Frame extends JFrame{
JProgressBar pgbMission;
JButton btnMission;
int count = 0;
public Exam20_13Frame(){
init();
private void init( ){
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
pgbMission = new JProgressBar();
btnMission = new JButton("点击10次");
pgbMission.setMinimum(0);//设置进度条最大值
pgbMission.setMaximum(10);//设置进度条最小值
pgbMission.setStringPainted(true);
pgbMission.setValue(0);
pgbMission.setSize(200, 30);
pgbMission.setLocation(30, 30);
pgbMission.setFont(new Font("微软雅黑", Font.PLAIN, 18));
pgbMission.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
if(pgbMission.getValue() == pgbMission.getMaximum()){
pgbMission.setString("任务完成!");
btnMission.setSize(120, 30);
btnMission.setLocation(250, 30);
btnMission.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btnMission.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pgbMission.setValue(++count);
container.add(pgbMission);
container.add(btnMission);
public class Exam20_13 {
public static void main(String[] args) {
Exam20_13Frame frame = new Exam20_13Frame();
frame.setSize(400, 150);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_13Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_13】的运行结果如图20-13所示。
图20-13【例20_13】运行结果
在图20-13所示的窗体上,每次单击“点击10次”按钮,进度条的蓝条就会增加10%,这是因为进度条的最小值是1,最大值是10,每次单击按钮都会调用JProgressBar的setValue()方法把当前值加1,当单击够10次后,进度条上就会显示“进度完成”,这是因为程序中给进度条添加了ChangeListener来监听进度条的变化,每当进度条的进度发生变化时,ChangeListener的事件处理方法stateChanged()就会执行,一旦进度条的当前值等于最大值时,就把进度条上的文本修改为“进度完成”。
进度条的另一种模式是不确定模式。确定模式的指进度条中的蓝条能够准确的反映出任务完成度的百分比,即便在进度条上没有显示出这个百分比的具体数值,通过蓝条的长度与进度条总长度也能体现出这个比例。但不确定模式的进度条并不能体现这种比例关系,这种进度条经常用于表示任务完成度不确定的情况下,例如在网络环境不稳定的情况下,从服务器下载一个较大的文件时可以考虑使用这种模式的进度条,这种进度条当中的蓝条是不停的在移动当中,表示任务正在进行,但无法体现出任务的完成度。程序员通过JprogressBar的setIndeterminate()方法就能把进度条设置为不确定模式,只需要给这个方法传递一个true就可以。由于不确定模式的进度条的作用并不是用来显示任务完成了多少,它只是告诉用户任务正在进行,因此对不确定模式的进度条设置最大值、最小值和当前值都不会改变它的外观。下面的【例20_14】展示了一个不确定模式进度条。
【例20_14 JprogressBar的使用2】
Exam20_14.java
import java.awt.*;
import javax.swing.*;
class Exam20_14Frame extends JFrame{
JProgressBar pgbIndeterminate;
public Exam20_14Frame(){
init();
private void init( ){
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
pgbIndeterminate = new JProgressBar();
pgbIndeterminate.setSize(200, 30);
pgbIndeterminate.setLocation(30, 30);
pgbIndeterminate.setIndeterminate(true);
container.add(pgbIndeterminate);
public class Exam20_14 {
public static void main(String[] args) {
Exam20_14Frame frame = new Exam20_14Frame();
frame.setSize(400, 150);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_14Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_14】的运行结果如图20-14所示。
图20-14【例20_14】运行结果
20.5.7列表框JList
列表框提供一组选项供用户选择,Swing体系中用JList类来表示列表框组件。列表框和文本区一样,它自身都不会带有边框,也不带滚动条,因此要通过代码为列表框添加边框和滚动条,其实现过程与给文本区添加边框和滚动条的方法跟文本区是一样的。
列表框只是一个展示选项的窗口,用户从列表框中看到的那些选项,其实是来自于一个选项的容器。这就好比一台电视机,电视机里播放的内容并不是来自于电视机本身,而是来自于某个电视台。从理论上来讲,程序员可以用三种类型的对象充当选项的容器,分别是数组、Vector和ListModel。如果使用ListModel作为选项容器,则需要用JList的setModel()方法使ListModel成为JList的选项容器。实际开发过程中主要使用ListModel作为选项容器。ListModel实际上是一个接口,而Swing体系中提供了一个默认的实现类DefaultListModel,它是使用频率最高的选项容器,之所以最常用DefaultListModel作为选项容器,是因为它提供了很多用于操作选项,如表20-7所示。
表20-7 DefaultListModel的方法
当用户选择了列表框中某些选项时,程序需要获取被选中项的索引,JList的getSelectedIndices()方法能够获得所有被选中选项的索引,该方法的返回值是一个int型数组,数组中的每一个元素都是一个被选中项的索引。下面的【例20_15】展示了列表框组件的使用方法。
【例20_15 JList的使用】
Exam20_15.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Exam20_15Frame extends JFrame{
JLabel lblSelect;
JList<String> lstLeft;
JList<String> lstRight;
JButton btnSelectAll;
JButton btnSelect;
JButton btnCancel;
JButton btnCancelAll;
DefaultListModel<String> leftData;
DefaultListModel<String> rightData;
public Exam20_15Frame() {
init();
private void init() {
Container container = this.getContentPane();
container.setLayout(null);
leftData = new DefaultListModel<String>();
rightData = new DefaultListModel<String>();
initLeftData();
lblSelect = new JLabel("你准备带谁出征?");
lstLeft = new JList<String>();
lstLeft.setModel(leftData);
lstRight = new JList<String>();
btnSelectAll = new JButton("全带");
btnSelect = new JButton("带走");
btnCancel = new JButton("撤销");
btnCancelAll = new JButton("重选");
lblSelect.setSize(200,40);
lblSelect.setLocation(30, 30);
lblSelect.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lstLeft.setSize(200,240);
lstLeft.setLocation(30, 80);
lstLeft.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lstLeft.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
lstLeft.addMouseListener(new MouseAdapter(){
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount()==2){
int index = lstLeft.getSelectedIndex();
rightData.addElement(leftData.remove(index));
lstRight.setSize(200,240);
lstRight.setLocation(350, 80);
lstRight.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lstRight.setBorder(BorderFactory.createLineBorder(Color.GRAY,1));
btnSelectAll.setSize(80,40);
btnSelectAll.setLocation(250, 90);
btnSelectAll.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnSelectAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int index = rightData.size();
for(int i=leftData.size()-1;i>=0;i--){
rightData.insertElementAt(leftData.remove(i),index);
lstRight.setModel(rightData);
btnSelect.setSize(80,40);
btnSelect.setLocation(250, 150);
btnSelect.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnSelect.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] idx = lstLeft.getSelectedIndices();
int index = rightData.size();
for(int i=idx.length-1;i>=0;i--){
rightData.insertElementAt(leftData.remove(idx[i]),index);
lstRight.setModel(rightData);
btnCancel.setSize(80,40);
btnCancel.setLocation(250, 210);
btnCancel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnCancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] idx = lstRight.getSelectedIndices();
int index = leftData.size();
for(int i=idx.length-1;i>=0;i--){
leftData.insertElementAt(rightData.remove(idx[i]),index);
lstLeft.setModel(leftData);
btnCancelAll.setSize(80,40);
btnCancelAll.setLocation(250, 270);
btnCancelAll.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnCancelAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
rightData.removeAllElements();
leftData.removeAllElements();
initLeftData();
container.add(lblSelect);
container.add(lstLeft);
container.add(lstRight);
container.add(btnSelectAll);
container.add(btnSelect);
container.add(btnCancel);
container.add(btnCancelAll);
private void initLeftData(){
leftData.addElement("关羽");
leftData.addElement("张飞");
leftData.addElement("赵云");
leftData.addElement("马超");
leftData.addElement("黄忠");
leftData.addElement("魏延");
public class Exam20_15 {
public static void main(String[] args) {
Exam20_15Frame frame = new Exam20_15Frame();
frame.setSize(600, 400);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_15Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_15】的运行结果如图20-15所示。
图20-15【例20_15】运行结果
在图20-15所示窗体上,直接单击“全带”按钮就可以把左边列表框中的所有选项移动到右边的列表框,而单击“重选”按钮又可把所有选项重新移动回左边的列表框。如果选中左边列表框中某些选项后单击“带走”按钮后就能把选中的选项移动到右边的列表框,而如果选中右边列表框中的某些选项后单击“撤销”按钮就能把这些选项重新移动回左边的列表框。
当对某些选中的选项进行移动时,实际上是通过remove()方法实现的,此时必须注意:用remove()方法删除选中项时需要倒序删除,这是因为正序删除会导致被删除项后面的选项索引发生变化。
20.5.8组合框JComBox
组合框是文本框和下拉列表组合起来的一个组件,它既能像下拉列表那样为用户提供一些选项,也可以像文本框那样,让用户把文本输入到其中,所以叫做组合框。Swing体系用JComboBox类来表示组合框。
组合框中可以出现若干选项,同列表框一样这些选项也可以来自于选项容器。组合框所使用的选项容器也是三种,分别是数组、Vector和ComboBoxModel。其中ComboBoxModel也是一个接口,一般情况下程序员都使用Swing体系中提供的DefaultComboBoxModel来作为真实的选项容器。DefaultComboBoxModel所定义的方法与DefaultListModel定义的操作选项的方法很相似,只是移除指定选项的方法变成了removeElementAt(),并且去掉了get()和elementAt()方法,只有getElementAt()方法用于获取指定的选项。需要注意:默认情况下组合框并不能让用户输入文本,如果希望能够让用户在组合框中输入文本,需要调用JComBox类的setEditable()方法并
如果希望获得被选中项的索引,可以调用JComBox的getSelectedIndex ()方法实现,而如果想获得被选中像本身,则可以调用getSelectedItem()。用户如果直接在组合框中填写了一些文本,那么getSelectedItem()方法获得的就是用户在组合框中填写的文本。下面的【例20_16】展示了用JComBox实现联动下拉框。
【例20_16 JComBox的使用】
Exam20_16.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Exam20_16Frame extends JFrame{
JLabel lblHome;
JComboBox<String> jcbProvince;
JComboBox<String> jcbCity;
DefaultComboBoxModel<String> provinceData;
DefaultComboBoxModel<String> cityData1;
DefaultComboBoxModel<String> cityData2;
DefaultComboBoxModel<String> cityData3;
public Exam20_16Frame() {
init();
private void init() {
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
lblHome = new JLabel("你的家乡是");
jcbProvince = new JComboBox<String>();
jcbCity = new JComboBox<String>();
provinceData = new DefaultComboBoxModel<String>();
provinceData.addElement("河北");
provinceData.addElement("河南");
provinceData.addElement("山东");
jcbProvince.setModel(provinceData);
cityData1 = new DefaultComboBoxModel<String>(new String[]{"石家庄","唐山","邯郸"});
cityData2 = new DefaultComboBoxModel<String>(new String[]{"郑州","开封","洛阳"});
cityData3 = new DefaultComboBoxModel<String>(new String[]{"济南","青岛","烟台"});
lblHome.setSize(200,40);
lblHome.setLocation(30, 30);
lblHome.setFont(new Font("微软雅黑", Font.PLAIN, 20));
jcbProvince.setSize(150,30);
jcbProvince.setLocation(30, 80);
jcbProvince.setFont(new Font("微软雅黑", Font.PLAIN, 20));
jcbProvince.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if(jcbProvince.getSelectedItem().equals("河北")){
jcbCity.setModel(cityData1);
}else if(jcbProvince.getSelectedItem().equals("河南")){
jcbCity.setModel(cityData2);
}else if(jcbProvince.getSelectedItem().equals("山东")){
jcbCity.setModel(cityData3);
jcbCity.setSize(150,30);
jcbCity.setLocation(200, 80);
jcbCity.setFont(new Font("微软雅黑", Font.PLAIN, 20));
jcbCity.setModel(cityData1);
container.add(lblHome);
container.add(jcbProvince);
container.add(jcbCity);
public class Exam20_16 {
public static void main(String[] args) {
Exam20_16Frame frame = new Exam20_16Frame();
frame.setSize(400, 250);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_16Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_16】的运行结果如图20-16所示。
图20-16【例20_16】运行结果
在图20-16所示的窗体上切换左边组合框的选项后,右边组合框中的选项也会随之变化。例如把左边的组合框切换为“河南”,则右边组合框中的选项会自动变成“郑州”,“开封”和“洛阳”。
20.5.9表格JTable
Swing体系中用JTable类表示表格。表格和列表框一样,它只是展现数据的窗口,表格中的数据都是由它的数据容器来存放和管理的。表格数据的容器一般使用TableModel充当,TableModel是一个接口,实际开发过程中一般使用DefaultTableModel类对象作为数据容器。如果表格中的数据很多,程序员需要把表格放在JScrollPane当中以产生滚动条。
表格中每一列数据都有特定意义,因此每个列必须定义一个“列头”,列头也被称为表头,当创建DefaultTableModel对象时需要以一个Object类的一维数组指定列头,而以一个二维Object类数组定义表格中的数据。
JTable定义了很多用于操作表格的方法,如表20-8所示。
表20-8 JTable类的方法
void addColumn(Object columnName, Object[] columnData)
添加一列数据,列名为columnName,列中的数据由columnData存储
int getRowCount()
获取表格的行数
int getColumnCount()
获取表格的列数
String getColumnName(int column)
获取索引为column的列名称
boolean isCellEditable(int row, int column)
获取索引为row的行column的列的单元格是否可以编辑
Object getValueAt(int row, int column)
获取索引为row的行column的列的单元格中的数据
setValueAt(Object aValue, int row, int column)
设置索引为row的行column的列的单元格中的数据为aValue
下面的【例20_17】展示了JTable的使用方法。
【例20_17 JTable的使用】
Exam20_17.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
class Exam20_17Frame extends JFrame{
JTable tblData;//表格
JScrollPane jspData;//滚动面板
JLabel lblNum, lblName,lblSex, lblAge;//标签
JTextField txtNum,txtName,txtAge;//文本框
JComboBox<String> jcbSex;//组合框
JButton btnAdd,btnUpdate,btnDelete,btnSelectAll,btnDeselectAll;//按钮
DefaultTableModel dm;
Object[][] data;
Object[] columns;
public Exam20_17Frame() {
init();
private void init() {
Container container = this.getContentPane();// 获得窗体的主体区域
container.setLayout(null);
//创建组件对象
data = new Object[][]{
{Boolean.valueOf(false),"1","张三","男","16"},
{Boolean.valueOf(false),"2","李四","男","17"},
{Boolean.valueOf(false),"3","王小花","女","16"}
columns = new Object[]{"删除选择","学号","姓名","性别","年龄"};
dm = new DefaultTableModel(data, columns){
public Class getColumnClass(int c){
if(c==0){
return Boolean.class;
}else{
return Object.class;
tblData = new JTable(dm){
public boolean isCellEditable(int row ,int column){
if(column==0){
return true;
}else{
return false;
};//创建表格
jspData = new JScrollPane(tblData);
lblNum = new JLabel("学号");
lblName = new JLabel("姓名");
lblSex = new JLabel("性别");
lblAge = new JLabel("年龄");
txtNum = new JTextField();
txtName = new JTextField();
jcbSex = new JComboBox<String>();
txtAge = new JTextField();
btnAdd = new JButton("新增");
btnUpdate = new JButton("修改");
btnDelete = new JButton("删除");
btnSelectAll = new JButton("全选");
btnDeselectAll = new JButton("全不选");
//初始化各组件基础属性
jspData.setSize(600, 150);
jspData.setLocation(100, 30);
tblData.setFont(new Font("微软雅黑", Font.PLAIN, 18));
tblData.setRowHeight(25);
tblData.getTableHeader().setFont(new Font("微软雅黑", Font.PLAIN, 18));
tblData.getColumnModel().getColumn(0).setPreferredWidth(150);
tblData.getColumnModel().getColumn(1).setPreferredWidth(100);
tblData.getColumnModel().getColumn(2).setPreferredWidth(150);
tblData.getColumnModel().getColumn(3).setPreferredWidth(100);
tblData.getColumnModel().getColumn(4).setPreferredWidth(100);
tblData.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tblData.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if(e.getValueIsAdjusting()==true){
int row = tblData.getSelectedRow();
String num = (String)tblData.getValueAt(row, 1);
String name = (String)tblData.getValueAt(row,2);
String sex = (String)tblData.getValueAt(row, 3);
String age = (String)tblData.getValueAt(row, 4);
txtNum.setText(num);
txtName.setText(name);
txtAge.setText(age);
jcbSex.setSelectedItem(sex);
lblNum.setSize(50,30);
lblNum.setLocation(100, 200);
lblNum.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txtNum.setSize(80,30);
txtNum.setLocation(150, 200);
txtNum.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lblName.setSize(50,30);
lblName.setLocation(250, 200);
lblName.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txtName.setSize(80,30);
txtName.setLocation(300, 200);
txtName.setFont(new Font("微软雅黑", Font.PLAIN, 20));
lblSex.setSize(50,30);
lblSex.setLocation(400, 200);
lblSex.setFont(new Font("微软雅黑", Font.PLAIN, 20));
jcbSex.setSize(80,30);
jcbSex.setLocation(450, 200);
jcbSex.setFont(new Font("微软雅黑", Font.PLAIN, 20));
jcbSex.addItem("男");
jcbSex.addItem("女");
lblAge.setSize(50,30);
lblAge.setLocation(550, 200);
lblAge.setFont(new Font("微软雅黑", Font.PLAIN, 20));
txtAge.setSize(80,30);
txtAge.setLocation(600, 200);
txtAge.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnAdd.setSize(80,30);
btnAdd.setLocation(100, 250);
btnAdd.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String num = txtNum.getText();
String name = txtName.getText();
String sex =(String) jcbSex.getSelectedItem();
String age = txtAge.getText();
boolean flag = validateAge(age);
if(flag == true){
dm.addRow(new Object[]{new Boolean(false),num,name,sex,age});
}else{
System.out.println("年龄值错误!");
btnUpdate.setSize(80,30);
btnUpdate.setLocation(200, 250);
btnUpdate.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnUpdate.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int row = tblData.getSelectedRow();
if(row==-1){
System.out.println("请选择数据!");
return;
String num = txtNum.getText();
String name = txtName.getText();
String sex =(String) jcbSex.getSelectedItem();
String age = txtAge.getText();
if(validateAge(age)==false){
System.out.println("年龄值错误!");
return;
tblData.setValueAt(num, row, 1);
tblData.setValueAt(name, row, 2);
tblData.setValueAt(sex, row, 3);
tblData.setValueAt(age, row, 4);
btnDelete.setSize(80,30);
btnDelete.setLocation(300, 250);
btnDelete.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnDelete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(validateSelection()==true){
for(int i=dm.getRowCount()-1;i>=0;i--){
if(dm.getValueAt(i, 0).equals(Boolean.TRUE)){
dm.removeRow(i);
}else{
System.out.println("请至少选择一条数据!");
btnSelectAll.setSize(80,30);
btnSelectAll.setLocation(400, 250);
btnSelectAll.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnSelectAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for(int i=0;i<dm.getRowCount();i++){
dm.setValueAt(new Boolean(true), i, 0);
btnDeselectAll.setSize(100,30);
btnDeselectAll.setLocation(500, 250);
btnDeselectAll.setFont(new Font("微软雅黑", Font.PLAIN, 20));
btnDeselectAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for(int i=0;i<dm.getRowCount();i++){
dm.setValueAt(Boolean.valueOf(false), i, 0);
//把组件添加到窗体中
container.add(jspData);
container.add(lblNum);
container.add(lblName);
container.add(lblSex);
container.add(lblAge);
container.add(txtNum);
container.add(txtName);
container.add(jcbSex);
container.add(txtAge);
container.add(btnAdd);
container.add(btnUpdate);
container.add(btnDelete);
container.add(btnSelectAll);
container.add(btnDeselectAll);
boolean validateAge(String age){
//不能为空
// 只能出现数字
//数字范围在10-20之间
if(age==null || age.equals("")){
return false;
for(int i=0;i<age.length();i++){
int chr = age.charAt(i);
if(chr<48 || chr>57){
return false;
int intAge = Integer.parseInt(age);//把字符串转换为整数
if(intAge<10||intAge>20){
return false;
return true;
boolean validateSelection(){
boolean selection = false;
for(int i=0;i<dm.getRowCount();i++){
if(dm.getValueAt(i, 0).equals(Boolean.TRUE)){
selection = true;
break;
return selection;
public class Exam20_17 {
public static void main(String[] args) {
Exam20_17Frame frame = new Exam20_17Frame();
frame.setSize(800, 350);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_17Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_17】的运行结果如图20-17所示。
图20-17【例20_17】运行结果
在图20-17所示的窗体上,在相应的文本框中填写数据后,单击“新增”按钮可以新增一条数据,而选择一行数据后,这行数据的每一个数据项都会显示到下面相应的文本框中,单击“修改”按钮可以修改所选中行的数据,而单击“删除”按钮可以删除选中的行,此外,窗体上还设置了“全选”和“全不选”两个按钮帮助用户快速选择批量数据。
20.5.10树形组件JTree
树形组件也是Swing体系中常用的组件之一。树形组件常用于表示有层次结构的数据结构,如同20-18所示。
图20-18树形结构
从图20-18可以看出,树形结构由多个节点组成,最顶端的节点称为“根节点”,除根节点外任何一个有下层节点的节点都称为“枝节点”,而没有下层节点的节点称为“叶节点”。从上下级关系来看,上层的节点称为“父节点”,而下层的节点都称之为“子节点”。
Swing体系中有一个能够表现树形图的组件,这个组件被称为“树形组件”。树形组件在Swing体系中用JTree类来表示,而树上的节点用DefaultMutableTreeNode类来表示,这个类也被称为“节点”类。节点类提供了一些用于操作节点的方法,如表20-10所示。
表20-10 DefaultMutableTreeNode的方法
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
class Exam20_18Frame extends JFrame{
JTree dynTree;
JScrollPane jspDyn;
DefaultMutableTreeNode treeRoot ,temp;
JTextField txtName;
JButton btnAdd,btnRename,btnDelete;
public Exam20_18Frame(){
init();
private void init( ){
Container container = this.getContentPane();//获得窗体的主体区域
container.setLayout(null);
treeRoot = new DefaultMutableTreeNode("根");
dynTree = new JTree(treeRoot);
jspDyn = new JScrollPane(dynTree);
txtName = new JTextField();
btnAdd = new JButton("新增");
btnRename = new JButton("修改");
btnDelete = new JButton("删除");
dynTree.setFont(new Font("微软雅黑", Font.PLAIN, 18));
jspDyn.setSize(350, 250);
jspDyn.setLocation(50, 50);
txtName.setSize(80, 30);
txtName.setLocation(50, 300);
txtName.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btnAdd.setSize(80, 30);
btnAdd.setLocation(140, 300);
btnAdd.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btnAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
temp = (DefaultMutableTreeNode)dynTree.getLastSelectedPathComponent();
String name = txtName.getText();
temp.add(new DefaultMutableTreeNode(name));
dynTree.updateUI();
btnRename.setSize(80, 30);
btnRename.setLocation(230, 300);
btnRename.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btnRename.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
temp = (DefaultMutableTreeNode)dynTree.getLastSelectedPathComponent();
String name = txtName.getText();
if(validate(temp,name)==true){
temp.setUserObject(name);
dynTree.updateUI();
btnDelete.setSize(80, 30);
btnDelete.setLocation(320, 300);
btnDelete.setFont(new Font("微软雅黑", Font.PLAIN, 18));
btnDelete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
temp = (DefaultMutableTreeNode)dynTree.getLastSelectedPathComponent();
if(temp==null){
System.out.println("请选中一个节点!");
return;
if(temp.isRoot()==true){
System.out.println("不能删除根节点!");
return;
temp.removeFromParent();
dynTree.updateUI();
container.add(jspDyn);
container.add(txtName);
container.add(btnAdd);
container.add(btnRename);
container.add(btnDelete);
boolean validate(DefaultMutableTreeNode node,String name){
if(node==null){
System.out.println("请选择一个节点!");
return false;
name = txtName.getText();
if(name.equals("")){
System.out.println("请填写节点的名称");
return false;
return true;
public class Exam20_18 {
public static void main(String[] args) {
Exam20_18Frame frame = new Exam20_18Frame();
frame.setSize(500, 450);
frame.setLocationRelativeTo(null);
frame.setTitle("Exam20_18Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
【例20_18】的运行结果如图20-19所示。
图20-19【例20_18】运行结果
在图20-19所示的窗体中,先单击根节点,然后在下方文本框中输入子节点的文本,单击“新增”按钮可以新增一个子节点,双击根节点后,可以展开该树,在该树上选择任意一个节点后输入子节点的文本再次单击“新增”按钮即可为选中的节点新增一个子节点。选择一个子节点后还可以修改和删除该节点。