GUI图形化界面(后篇)

1.1 Swing概述

Swing是由100%纯java实现的,不再依赖于本地平台的GUI,因此可以在所有平台上都保持相同的界面外观。独立于本地平台的Swing组件被称为轻量级组件;而依赖于本地平台的AWT组件被称为重量级组件。
由于Swing的所有组件完全采用java实现,不再调用本地平台的GUI,所以导致Swing图形化界面的显示速度要比AWT图形界面的显示速度慢一些,但相对于快速发展的硬件设施而言,这种微小的差别无妨大碍。
使用Swing的优势:
1.Swing组件不再依赖于本地平台的GUI,无需采用各种平台的GUI交集,因此Swing提供了大量图形界面,远远超出了AWT所提供的的图形界面组件集;
2.Swing组件不再依赖于本地平台的GUI,因此不会产生与平台相关的bug。
3.Swing组件在各种平台上运行时可以保证具有相同的图形界面外观。
Swing提供的这些优势,让java图形界面程序真正实现"Write Once,Run Anywhere"的目标。
Swing的特征:
1.Swing组件采用MVC(Model-View-Controller,即模型-视图-控制器)设计模式:

模型(Model):用于维护组件的各种状态;
视图(View):是组件的可视化表现;
控制器(Controller):用于控制对于各种事件、组件做出响应。
当模型发生改变时,它会通知所有依赖它的视图,视图会根据模型数据来更新自己。Swing使用UI代理来包装视图和控制器,还有一个模型对象来维护该组件的状态,例如,按钮JButton有一个维护其状态信息的模型ButtonModel对象。Swing组件的模型是自动设置的,因此一般都使用JButton,而无须关心Button对象。
2.Swing在不同平台上表现一致,并且有能力提供本地平台不支持的显示外观。由于Swing采用MVC模式来维护各组件,所以当组件的外观被改变时,对组件的转态信息(由模型维护没)有任何影响。因此,Swing可以使用插拔式外观感觉(Pluggable Lock And Feel,PLAF)来控制外观,使得Swing图形界面在同一平台上运行时能拥有不同的外观,用户可以选择自己喜欢的外观。相比之下,在AWT图形界面中,由于控制组件外观的对等类与具体平台相关,因此AWT组件总是具有与本地平台相同的外观。

1.2 Swing基本组件的用法

1.2.1 Swing组件层次

Swing组件继承体系图:
Swing组件继承体系图
Swing组件和AWT组件的对于关系:
1.JComBox:对于AWT里的Choice组件;
2.JFileChooser:对应于AWT里的FileDialog;
3.JScrollBar:对应于AWT里的Scrollbar;
4.JCheckBox:对应Checkbox;
5.JCheckBoxMenuItem:对应CheckboxMenuItem。
Swing组件按照功能来分类:
1.顶层容器:JFrame、JApplet、JDialog和Window。
2.中间容器:JPanel、JScrollPane、JSplitPane、JToolBar等。
3.特殊容器:在用户界面上具有特殊作用的中间容器,如JintemalFrame、JRootPane、JLayeredPane和JDestopPane等。
4.基本组件:实现人机交互的组件,如JButton、JComboBox、JList、JMenu、JSlider等。
5.不可编辑信息的显示组件:向用户显示不可编辑信息的组件,如JLabel、JProgressBar和JToolTip等。
6.可编辑信息的显示组件:向用户显示能被编辑的格式化信息的组件,如JTable、JTextArea和JTextField等。
7.特殊对话框组件:可以直接产生特殊对话框的组件,如JColorChooser和JFileChooser等。

1.2.2 AWT组件的Swing的实现

Swing为除Canvas之外的所有AWT组件提供了相应的实现,Swing组件比AWT组件的功能更加强大。相比对于AWT组件,Swing组件具有如下4个额外的功能:
1.可以为Swing组件设置提示信息。使用setToolText()方法,为组件设置对用户有帮助的提示信息。
2.很多Swing组件例如按钮、标签、菜单项等,除使用文字外,还可以使用图标修饰自己。为了允许在Swing组件中使用图标,Swing为Icon接口提供了一个实现类:ImageIcon,该实现类代表一个图像图标。
3.支持插拔式的外观风格。每个JCommponent对象都有一个相应的ComponentUI对象,为它完成所有的绘画、事件处理、决定尺寸大小等工作。Component()对象依赖当前使用的PLAF,使用UIManager…setLookAndFeel()方法可以改变图形界面的外观风格。
4.支持设置边框。Swing组件可以设置一个或多个边框。Swing中提供了各式各样的边框供用户使用,也能建立组合边框或自己设置边框。一种空白边框可以用于增大组件,同时协助布局管理器对容器中的组件进行合理的布局。

每个Swing组件都有一个对应的UI类,例如JButton组件就有一个对应的ButtonUI类来作为UI代理。每个Swing组件的UI代理的类总是将该Swing组件类名的J去掉,然后在后面添加UI后缀,UI代理通常是一个抽象基类,不同的PLAF会有不同的UI代理实现类。Swing类库中包含了几套UI代理,分别放在不同的包下,每套UI代理都几乎包含了所有的Swing组件的ComponentUI实现,每套这样的实现都称为一种PLAF实现。

//容器
JFrame jf = new JFrame();
try{
//设置外观风格
UIManager.setLookAndFeel("com.sun.java.plaf.windows.windowsLookAndFeel");
//刷新jf容器及其内部组件的外观
SwingUtilities.updateComponentTreeUI(jf);
} catch (Exception e) {
e.printStackTrace();
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class SwingComponentDemo {
    JFrame f = new JFrame("测试Swing基本组件");

    //声明菜单相关的组件
    JMenuBar menuBar = new JMenuBar();

    JMenu fileMenue = new JMenu("文件");
    JMenu editMenue = new JMenu("编辑");

    JMenuItem auto = new JMenuItem("自动换行");
    JMenuItem copy = new JMenuItem("复制",new ImageIcon("swing-app\\img\\component\\copy.png"));
    JMenuItem paste = new JMenuItem("粘贴",new ImageIcon("swing-app\\img\\component\\paste.png"));

    JMenu formatMenu = new JMenu("格式");
    JMenuItem comment = new JMenuItem("注释");
    JMenuItem cancelComment = new JMenuItem("取消注释");

    //声明文本域
    JTextArea ta = new JTextArea(8, 20);

    String[] colors= {"红色","绿色","蓝色"};
    //声明列表框
    JList<String> colorList = new JList<String>(colors);

    //声明选择相关组件
    JComboBox<String> colorSelect = new JComboBox<String>();

    ButtonGroup bg = new ButtonGroup();
    JRadioButton male = new JRadioButton("男", false);
    JRadioButton female = new JRadioButton("女", true);

    JCheckBox isMarried = new JCheckBox("是否已婚?", true);

    //声明底部
    JTextField tf = new JTextField(40);
    JButton ok = new JButton("确定",new ImageIcon("swing-app\\img\\component\\ok.png"));

    //声明右键菜单
    JPopupMenu jPopupMenu = new JPopupMenu();

    ButtonGroup poupButtonBg = new ButtonGroup();

    JRadioButtonMenuItem metalItem = new JRadioButtonMenuItem("Metal 风格");
    JRadioButtonMenuItem nimbusItem = new JRadioButtonMenuItem("Nimbus 风格");
    JRadioButtonMenuItem windowsItem = new JRadioButtonMenuItem("Windows 风格",true);
    JRadioButtonMenuItem windowsClassicItem = new JRadioButtonMenuItem("Windows 经典风格");
    JRadioButtonMenuItem motifItem = new JRadioButtonMenuItem("Motif 风格");


    //初始化界面
    public void init() {
        //组装视图

        //组装底部
        JPanel bottomPanel = new JPanel();
        bottomPanel.add(tf);
        bottomPanel.add(ok);

        f.add(bottomPanel, BorderLayout.SOUTH);

        //组装选择相关的组件
        JPanel selectPanel = new JPanel();

        colorSelect.addItem("红色");
        colorSelect.addItem("绿色");
        colorSelect.addItem("蓝色");

        selectPanel.add(colorSelect);
        bg.add(male);
        bg.add(female);
        selectPanel.add(male);
        selectPanel.add(female);

        selectPanel.add(isMarried);
        
        //组装文本域和选择相关组件
        Box topLeft = Box.createVerticalBox();
        topLeft.add(ta);
        topLeft.add(selectPanel);

        //组装顶部
        Box top = Box.createHorizontalBox();
        top.add(topLeft);
        top.add(colorList);

        f.add(top);

        //组装顶部菜单
        formatMenu.add(comment);
        formatMenu.add(cancelComment);

        editMenue.add(auto);
        editMenue.addSeparator();
        editMenue.add(copy);
        editMenue.add(paste);
        editMenue.addSeparator();
        editMenue.add(formatMenu);

        menuBar.add(fileMenue);
        menuBar.add(editMenue);

        f.setJMenuBar(menuBar);

        //组装右键菜单
        poupButtonBg.add(metalItem);//如果没有组装,显示有一些区别,并不会知道点击类哪一个
        poupButtonBg.add(nimbusItem);
        poupButtonBg.add(windowsItem);
        poupButtonBg.add(windowsClassicItem);
        poupButtonBg.add(motifItem);

        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //当前选择的是哪一个风格
                String actionCommand = e.getActionCommand();
                try {
                    changeFlavor(actionCommand);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        };

        metalItem.addActionListener(listener);
        nimbusItem.addActionListener(listener);
        windowsItem.addActionListener(listener);
        windowsClassicItem.addActionListener(listener);
        motifItem.addActionListener(listener);

        jPopupMenu.add(metalItem);
        jPopupMenu.add(nimbusItem);
        jPopupMenu.add(windowsItem);
        jPopupMenu.add(windowsClassicItem);
        jPopupMenu.add(motifItem);

        //不需要再监听鼠标事件了
        ta.setComponentPopupMenu(jPopupMenu);


        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口
        f.pack();
        f.setVisible(true);

    }

    public static void main(String[] args) {
        new SwingComponentDemo().init();
    }


    //定义一个方法,用于改变界面风格
    private void changeFlavor(String command) throws Exception {
        switch (command) {
            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(f.getContentPane());
        SwingUtilities.updateComponentTreeUI(menuBar);
        SwingUtilities.updateComponentTreeUI(jPopupMenu);
    }

}

1.2.3 为组件设置边框

很多情况下,我们常常喜欢给不同的组件设置边框,从而让界面的层次感更明显,swing中提供了Border对象来代表一个边框,下图是Border的继承体系图:
Border的继承体系图
特殊的Border:
1.TitleBorder:它的作用并不是直接为其他组件添加边框,而是为其他边框设置标题,创建类的对象时,需要传入一个其他的Border对象;
2.CompoundBorder:用来组合其他两个边框,创建该类的对象时,需要传入其他两个Border对象一个作为内边框,一个作为外边框。
给组件设置边框步骤:
1.使用BorderFactory或者XxxBorder创建Border的实例对象;
2.调用Swing组件的setBorder(Border b)方法为组件设置边框;

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;

public class BorderTest {

    JFrame jf  = new JFrame("测试边框");

    public void init(){
        //组装视图
        //1.JFrame的布局修改为GridLayout
        jf.setLayout(new GridLayout(2,4));

        //2.往网格中填充不同的JPanel组件,并且设置边框和内容

        //创建BevelBorder
        Border bevelBorder = BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.RED,
                Color.GREEN, Color.BLUE, Color.GRAY);
        jf.add(getJPanelWithBorder(bevelBorder,"BevelBorder"));

        //创建LineBorder
        Border lineBorder = BorderFactory.createLineBorder(Color.ORANGE, 10);
        jf.add(getJPanelWithBorder(lineBorder,"LineBorder"));

        //创建EmptyBorder
        Border emptyBorder = BorderFactory.createEmptyBorder(10, 5, 20, 10);
        jf.add(getJPanelWithBorder(emptyBorder,"EmptyBorder"));

        //创建EchtedBorder
        Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.RED, Color.GREEN);
        jf.add(getJPanelWithBorder(etchedBorder,"EtchedBorder"));

        //创建TitledBorder
        TitledBorder titledBorder = new TitledBorder(new LineBorder(Color.ORANGE,10),
                "测试标题",TitledBorder.LEFT,TitledBorder.BOTTOM,new Font("StSong",Font.BOLD,18),Color.BLUE);
        jf.add(getJPanelWithBorder(titledBorder,"TitledBorder"));

        //创建MatteBorder
        MatteBorder matteBorder = new MatteBorder(10, 5, 20, 10, Color.GREEN);
        jf.add(getJPanelWithBorder(matteBorder,"MatteBorder"));

        //创建CompoundBorder
        CompoundBorder compoundBorder = new CompoundBorder( new LineBorder(Color.RED, 10),titledBorder);
        jf.add(getJPanelWithBorder(compoundBorder,"CompoundBorder"));

        //3.设置窗口最佳大小、设置窗口可见,处理关闭操作
        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();
    }
}

1.2.4 使用JToolBar创建工具条

Swing提供了JToolBar类来创建工具条,并且可以往JToolBar中添加多个工具按钮。
JToolBar常用API:

方法名称方法功能
JToolBar(String name, int orientation)创建一个名字为name,方向为orientation的工具条对象,其orientation的取值可以是SwingConstants.HORIZONTAL 或SwingConstants.VERTICAL
JButton add(Action a)通过Action对象为ToolBar工具条添加对应的工具按钮
addSeparator(Dimension size)向工具条中添加指定大小的分隔符
setFloatable(boolean b)设定工具条是否可以拖动
setMargin(Insets m)设置工具条与工具按钮的边距
setOrientation(int o)设置工具条的方向

add(Action a)方法:
之前说过,Action接口是ActionListener的一个子接口,那么它代表一个事件监听器,而这里add方法是在给工具条添加一个工具按钮,为什么传递的是一个事件监听器?
首先,要明确的是不管是菜单条中的菜单项还是工具条中的工具按钮,最终确定需要点击来完成一些操作,所以ToolBar以及JMenu都提供了更加便捷的方法add(Action a),这个方法的内部都会做一下几件事:
1.创建一个适用于该容器的组件(例如:在工具栏中创建一个工具按钮);
2.从Action对象中获得对应的属性来设置该组件(通过Icon来设置按钮图标);
3.把Action监听器注册到刚才创建的组件上。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class JToolBarTest {

    JFrame jf = new JFrame("测试工具条");
    JTextArea jta = new JTextArea(6,35);

    //声明工具条相关内容

    JToolBar jToolBar = new JToolBar("播放工具条",SwingConstants.HORIZONTAL);

    //创建3个Action对象
    //传递的参数,name和icon,最终在添加到工具条中时,会被拿出来作为按钮的名称和图标
    Action pre = new AbstractAction("上一曲",new ImageIcon("swing-app\\img\\component\\pre.png")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jta.append("上一曲.\n");
        }
    };

    Action pause = new AbstractAction("暂停",new ImageIcon("swing-app\\img\\component\\pause.png")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jta.append("暂停播放.\n");
        }
    };

    Action next = new AbstractAction("下一曲",new ImageIcon("swing-app\\img\\component\\next.png")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jta.append("下一曲.\n");
        }
    };


    public void init(){
        //组装视图

        //通过Action对象来创建JButton

        JButton preBtn = new JButton(pre);
        JButton pauseBtn = new JButton(pause);
        JButton nextBtn = new JButton(next);


        /*
        jToolBar.add(pre);
        jToolBar.addSeparator();
        jToolBar.add(pause);
        jToolBar.addSeparator();
        jToolBar.add(next);
        */

        jToolBar.add(preBtn);
        jToolBar.addSeparator(new Dimension(10,10));
        jToolBar.add(pauseBtn);
        jToolBar.addSeparator(new Dimension(10,10));
        jToolBar.add(nextBtn);

        //让工具条可以拖动
        jToolBar.setFloatable(true);

        jf.add(jToolBar,BorderLayout.NORTH);

        //文本框默认不支持滚动条

        //把一个组件设置到JScrollPane中,那么该组件就支持滚动条了

        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();
    }
}

1.2.5 JColorChooser和JFileChooser

Swing提供了JColorChooser和JFileChooser这两种对话框,可以方便的完成颜色的选择和本地文件的选择。

1.2.5.1 JColorChooser

JColorChooser用于创建颜色选择器对话框,该类的用法非常简单,只需要调用它的静态方法就可以快速生成一个颜色对话框:

public static Color showDialog(Component component, String title, Color initialColor)
/*
	参数:
	component:指定当前对话框的父组件
	title:当前对话框的名称
	initialColor:指定默认选中的颜色
	
	返回值:
	返回用户选中的颜色
*/

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class JColorChooserDemo {

    JFrame jFrame = new JFrame("测试颜色选择器");

    JTextArea jta = new JTextArea("我爱中华",6,30);

    //声明按钮
    JButton btn = new JButton(new AbstractAction("改变文本框背景颜色") {
        @Override
        public void actionPerformed(ActionEvent e) {
            //弹出一个颜色选择器,
            Color result = JColorChooser.showDialog(jFrame, "颜色选择器", Color.blue);

            //修改文本框背景
            jta.setBackground(result);
        }
    });


    public void init(){

        //组装视图
        jFrame.add(jta);

        jFrame.add(btn,BorderLayout.SOUTH);


        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.pack();
        jFrame.setVisible(true);
    }

    public static void main(String[] args) {
        new JColorChooserDemo().init();
    }

}

1.2.5.2 JFileChooser

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.打开文件对话框

int showOpenDialog(Component parent)//:打开文件加载对话框,并指定父组件
int showSaveDialog(Component parent)//:打开文件保存对话框,并指定父组件

4.获取用户选择的结果

File getSelectedFile()//获取用户选择的一个文件
File[] getSelectedFiles()//获取用户选择的多个文件
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


public class JFileChooserDemo {

    //创建窗口对象
    JFrame jf = new JFrame("测试JFileChooser");

    //创建菜单条
    JMenuBar jmb = new JMenuBar();
    //创建菜单
    JMenu jMenu = new JMenu("文件");


    JMenuItem open = new JMenuItem(new AbstractAction("打开") {
        @Override
        public void actionPerformed(ActionEvent e) {
            //显示一个文件选择器
            JFileChooser fileChooser = new JFileChooser(".");
            fileChooser.showOpenDialog(jf);

            //获取用户选择的文件
            File file = fileChooser.getSelectedFile();

            //进行显示
            try {
                image = ImageIO.read(file);
                drawArea.repaint();
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }
    });

    JMenuItem save = new JMenuItem(new AbstractAction("另存为") {
        @Override
        public void actionPerformed(ActionEvent e) {
            //显示一个文件选择器
            JFileChooser fileChooser = new JFileChooser(".");
            fileChooser.showSaveDialog(jf);

            //获取用户选择的保存文件路径
            File file = fileChooser.getSelectedFile();

            try {
                ImageIO.write(image,"jpeg",file);
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }
    });


    BufferedImage image;

    //swing提供的组件,都使用了图像缓冲区技术
    private class MyCanvas extends JPanel{
        @Override
        public void paint(Graphics g) {
            //把图片绘制到组件上即可
            g.drawImage(image,0,0,null);
        }
    }

    MyCanvas drawArea = new MyCanvas();


    public void init(){
        //组装视图
        jMenu.add(open);
        jMenu.add(save);

        jmb.add(jMenu);

        jf.setJMenuBar(jmb);

        drawArea.setPreferredSize(new Dimension(740,500));
        jf.add(drawArea);

        //显示jf
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);
    }

    public static void main(String[] args) {
        new JFileChooserDemo().init();
    }

}

1.2.6 使用JOptionPane

1.2.6.1 基本概述

通过JOptionPane可以非常方便地创建一些简单的对话框,Swing已经为对话框添加了相应的组件,无须程序员手动添加组件。JOptionPane提供了如下4个方法来创建对话框。

方法名称方法功能
static void showMessageDialog/showInternalMessageDialog消息对话框,告知用户某事已发生,用户只能单击“确定”按钮,类似于JavaScript的alert函数
static int showConfirmDialog/showInternalConfirmDialog确认对话框,向用户确认某个问题,用户可以选择yes、no-cancel等选项。类似于JavaScript的comfirm函数。该方法返回用户单击了哪个按钮
static String showInputDialog/showInternalInputDialog输入对话框,提示要求输入某些信息,类似于JavaScript的prompt函数。该方法返回用户输入的字符串
static intshowOptionDialog/showOptionDialog自定义选项对话框,允许使用自定义选项,可以取代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_CANCEL_OPTION、YES_NO_OPTION、OK_CANCEL_OPTION等
messageType:当前对话框类型:ERROR_MESSAGE、INFORMATION_MESSAGE、WARNING_MESSAGE、PLAIN_MESSAGE、QUESTION_MESSAGE
icon:当前对话框左上角的图标
options:自定义下拉列表的选项
initialValue:自定义选项中的默认值

信息对话框:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

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();

            //参数:int messageType  指定消息对话框的类型: 错误消息,警告消息,问题消息...
            //JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.ERROR_MESSAGE);
            //JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.INFORMATION_MESSAGE);
            //JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.WARNING_MESSAGE);
            //JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.QUESTION_MESSAGE);
            //JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.PLAIN_MESSAGE);

            JOptionPane.showMessageDialog(jf,text,"消息对话框",JOptionPane.WARNING_MESSAGE,new ImageIcon("swing-app\\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();
    }

}

确认对话框:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class ConfirmDialogTest {


    JFrame jf = new JFrame("测试确认对话框");

    JTextArea jta = new JTextArea(6, 30);

    JButton btn = new JButton(new AbstractAction("弹出确认对话框") {

        @Override
        public void actionPerformed(ActionEvent e) {
            //TODO 弹出确认对话框
            String text = jta.getText();
            jta.append("\n");
            //int result = JOptionPane.showConfirmDialog(jf, text, "确认对话框", JOptionPane.DEFAULT_OPTION);
            //int result = JOptionPane.showConfirmDialog(jf, text, "确认对话框", JOptionPane.YES_NO_OPTION);
            int result = JOptionPane.showConfirmDialog(jf, text, "确认对话框", JOptionPane.YES_NO_CANCEL_OPTION);
            //int result = JOptionPane.showConfirmDialog(jf, text, "确认对话框", JOptionPane.OK_CANCEL_OPTION);
                if (result == JOptionPane.YES_OPTION){
                    jta.append("用户点击了 是 按钮\n");
            }

            if (result == JOptionPane.NO_OPTION){
                jta.append("用户点击了 否 按钮\n");
            }

            /*if (result == JOptionPane.OK_OPTION){
                jta.append("用户点击了 确认 按钮\n");//确认和是不会同时存在
            }*/

            if (result == JOptionPane.CANCEL_OPTION){
                jta.append("用户点击了 取消 按钮\n");
            }

        }
    });


    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 ConfirmDialogTest().init();
    }

}

输入对话框:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class InputDialogTest {


    JFrame jf = new JFrame("测试输入对话框");

    JTextArea jta = new JTextArea(6, 30);

    JButton btn = new JButton(new AbstractAction("弹出输入对话框") {

        @Override
        public void actionPerformed(ActionEvent e) {
            //弹出输入对话框
            String result = JOptionPane.showInputDialog(jf, "请填写您的银行账号:", "输入对话框", JOptionPane.INFORMATION_MESSAGE);
            jta.append(result);
        }
    });


    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 InputDialogTest().init();
    }
}

选项对话框:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class OptionDialogTest {


    JFrame jf = new JFrame("测试选项对话框");

    JTextArea jta = new JTextArea(6, 30);

    JButton btn = new JButton(new AbstractAction("弹出选项对话框") {

        @Override
        public void actionPerformed(ActionEvent e) {
           //弹出选项对话框
            int result = JOptionPane.showOptionDialog(jf, "请选择尿不湿号码", "选项对话框", JOptionPane.DEFAULT_OPTION,
                    JOptionPane.INFORMATION_MESSAGE, null, new String[]{"大号", "中号", "小号"}, "中号");

            switch (result){
                case 0:
                    jta.setText("用户选择了大号");
                    break;
                case 1:
                    jta.setText("用户选择了中号");
                    break;
                case 2:
                    jta.setText("用户选择了小号");
                    break;
            }

        }
    });


    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 OptionDialogTest().init();
    }

}

1.3 Swing中的特殊容器

Swing提供了一些具有特殊功能的容器,这些特殊容器可以用于创建一些更复杂的用户界面。

1.3.1 使用JSplitPane

JSplitPane用于创建一个分割面板,它可以将一个组件(通常是一个容器)分割成两部分,并提供一个分隔条,用户可以拖动该分割条来调整两个部分的大小。
JSplitPane使用步骤:
1.创建JSplitPane对象

  • 通过如下构造方法可以创造JSplitPane对象
    JSplitPane(int newOrientation, Component newLeftComponent, Component newRightComponent)
     newOrientation:指定JSplitPane容器的分割方向:
      如果值为JSplitPane.VERTICAL_SPLIT,为纵向分割;
      如果值为JSplitPane.HORIZONTAL_SPLIT,为横向分割;

 newLeftComponent:左侧或者上侧组件;
 newRightComponent:右侧或者下侧组件;
2.设置是否开启连续布局的支持(可选)
setContinuousLayout(boolean newContinuousLayout):
  默认是关闭的,如果设置为true,则打开连续布局的支持,但由于连续布局支持需要不断的重绘组件,所以效率会低一些
3.设置是否支持“一触即展”(可选)
setOneTouchExpandable(boolean newValue):
  默认是关闭的,如果设置为true,则打开“一触即展”的支持。
4.其他设置
setDividerLocation(double proportionalLocation):设置分隔条的位置为JSplitPane的某个百分比
setDividerLocation(int location):通过像素值设置分隔条的位置
setDividerSize(int newSize):通过像素值设置分隔条的大小
setLeftComponent(Component comp)/setTopComponent(Component comp)/setRightComponent(Component comp)/setBottomComponent(Component comp):设置指定位置的组件

import javax.swing.*;

public class Book {

    private String name;

    private Icon icon;

    private String desc;//书的描述

    public Book(String name, Icon icon, String desc) {
        this.name = name;
        this.icon = icon;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Icon getIcon() {
        return icon;
    }

    public void setIcon(Icon icon) {
        this.icon = icon;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return name;
    }
}
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;


public class SplitPaneTest {

    Book[] books = {new Book("java自学宝典", new ImageIcon("swing-app\\img\\container\\java.png"), "国内关于 Java 编程最全面的图书 \n 看得懂 , 学得会"),

            new Book("轻量级的JAVAEE企业应用实战", new ImageIcon("swing-app\\img\\container\\ee.png"), "SSM整合开发的经典图书,值的拥有"),

            new Book("Android基础教程", new ImageIcon("swing-app\\img\\container\\android.png"), "全面介绍Android平台应用程序\n 开发的各方面知识")

    };

    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));

        //为jList设置条目选中监听器
        bookJList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                //获取当前选中的是哪个条目
                Book book = bookJList.getSelectedValue();
                //把书籍的图片用bookCover展示
                bookCover.setIcon(book.getIcon());

                //把书籍的描述用bookDesc展示
                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();
    }

}

1.3.2 使用JTabbedPane

JTabbedPane可以很方便地在窗口上放置多个标签页,每个标签页相当于获得了一个与外部容器具有相同大小的组件摆放区域。通过这种方式,就可以在一个容器里放置更多的组件,例如右击桌面上的“我的电脑”图标,在弹出的快捷菜单里单击“属性”菜单项,就可以看到一个“系统属性”对话框,这个对话框包含了若干标签页。
JTabbedPane创建步骤:
1.创建JTabbedPane对象
JTabbedPane(int tabPlacement, int tabLayoutPolicy)
 tabPlacement:
  指定标签标题的放置位置,可以选择SwingConstants中的四个常量:TOP、LEFT、BOTTOM、RIGHT
 tabLayoutPolicy:
  指定当窗口不能容纳标签页标题时的布局策略,可以选择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的其他属性
setDisabledIconAt(int index, Icon disabledIcon):将指定位置的禁用图标设置为Icon,该图标也可以是null表示不使用禁用图标
setEnabledAt(int index, boolean enabled):设置指定位置标签页是否启用
setTitleAt(int index, String title):设置指定位置标签页的标题为Title,该title可以是null,这表明设置该标签页的标题为空
setToolTipTextAt(int index, String toolTipText):设置指定位置标签页的提示文本

5.为JTabbedPane设置监听器
addChangeListener(ChangeListener l)

import javax.swing.*;
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-app\\img\\container\\open.gif"),
                new JList<String>(new String[]{"用户一","用户二","用户三"}),"所有用户");
        tabbedPane.addTab("商品管理",new JList<String>(new String[]{"商品一","商品二","商品三"}));
        tabbedPane.addTab("订单管理",new ImageIcon("swing-app\\img\\container\\open.gif"),
                new JList<String>(new String[]{"订单一","订单二","订单三","订单四"}),"所有订单");



        //完成设置
        tabbedPane.setEnabledAt(1,false);

        tabbedPane.setSelectedIndex(0);


        //监听当前标签面板的选中情况
        tabbedPane.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent 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();
    }
}

1.4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条

  进度条是图形界面中广泛使用的GUI组件,当复制一个较大的文件时,操作系统会显示一个进度条,用于标识复制操作完成的比例:当启动Eclispe等程序时,因为需要加载较多资源,故而启动速度较慢,程序也会在启动过程中显示一个进度条,用以表示该软件启动完成的比例。

1.4.1创建进度条

使用JProgressBar创建进度条步骤:
1.创建JProgressBar对象
public JProgressBar(int orient, int min, int max):
 orient:方向
 min:最小值
 max:最大值

2.设置属性
setBorderPainted(boolean b):设置进度条是否有边框
setIndeterminate(boolean newValue):设置当前进度条是不是进度不确定的进度条,如果是,则看得一个滑块在进度条中左右移动
setStringPainted(boolean b):设置进度条是否显示当前完成的百分比

3.获取和设置当前精度条的进度状态
setValue(int n):设置当前的进度值
double getPercentComplete():获取进度条的完成百分比
String getString():返回进度字符串的当前值

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class JProgressTest {

    JFrame jf = new JFrame("测试进度条");

    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");

    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);


    public void init(){
        //组装视图

        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下indeterminate复选框有没有选中?
                boolean selected = indeterminate.isSelected();

                //设置当前进度条不确定进度
                bar.setIndeterminate(selected);

                bar.setStringPainted(!selected);

            }
        });

        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下noBorder复选框有没有选中?
                boolean selected = noBorder.isSelected();

                bar.setBorderPainted(!selected);
            }
        });

        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);


        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);

        //把当前窗口的布局方式修改为FlowLayout
        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();
    }
}

利用子线程来实现进度条:

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class JProgressTest2 {

    JFrame jf = new JFrame("测试进度条");

    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");

    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);


    public void init(){
        //组装视图

        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下indeterminate复选框有没有选中?
                boolean selected = indeterminate.isSelected();

                //设置当前进度条不确定进度
                bar.setIndeterminate(selected);

                bar.setStringPainted(!selected);

            }
        });

        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下noBorder复选框有没有选中?
                boolean selected = noBorder.isSelected();

                bar.setBorderPainted(!selected);
            }
        });

        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);


        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);

        //把当前窗口的布局方式修改为FlowLayout
        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(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(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                int value = bar.getValue();
                if (value == simulaterActivity.getAmount()){
                    timer.stop();
                }
            }
        });


    }

    private class SimulaterActivity implements  Runnable{
        //记录任务总量
        private int amount;

        //记录当前任务的完成量
        private volatile int current;//volatile:内存中可见

        public SimulaterActivity(int amount) {
            this.amount = amount;
        }

        public int getAmount() {
            return amount;
        }

        public void setAmount(int amount) {
            this.amount = amount;
        }

        public int getCurrent() {
            return current;
        }

        public void setCurrent(int current) {
            this.current = current;
        }

        @Override
        public void run() {
            //子线程的任务  模拟耗时操作
            while(current<amount){
                try {
                    Thread.currentThread().sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                current++;
            }
        }
    }

    public static void main(String[] args) {
        new JProgressTest2().init();
    }
}

JProgressBar采用了MVC思想:
JProgressBar组件有一个内置的用于保存其状态数据的Model对象,这个对象由BoundedRangeModel对象表示,程序调用JProgressBar对象完成进度百分比的设置,监听进度条的数据变化,其实都是通过它内置的BoundedRangeModel对象完成的。下面通过改进之前的代码,通过BoundedRangeModel完成数据的设置,获取与监听。

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class JProgressTest3 {

    JFrame jf = new JFrame("测试进度条");

    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");

    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);

    //获取处进度条内置的数据模型对象
    BoundedRangeModel model = bar.getModel();

    public void init(){
        //组装视图

        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下indeterminate复选框有没有选中?
                boolean selected = indeterminate.isSelected();

                //设置当前进度条不确定进度
                bar.setIndeterminate(selected);

                bar.setStringPainted(!selected);

            }
        });

        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取一下noBorder复选框有没有选中?
                boolean selected = noBorder.isSelected();

                bar.setBorderPainted(!selected);
            }
        });

        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);


        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);

        //把当前窗口的布局方式修改为FlowLayout
        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(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);
                model.setValue(current);
            }
        });
        timer.start();

        //监听进度条的任务变化
        model.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                int value = model.getValue();
                if (value == simulaterActivity.getAmount()){
                    timer.stop();
                }
            }
        });


    }

    private class SimulaterActivity implements  Runnable{
        //记录任务总量
        private int amount;

        //记录当前任务的完成量
        private volatile int current;

        public SimulaterActivity(int amount) {
            this.amount = amount;
        }

        public int getAmount() {
            return amount;
        }

        public void setAmount(int amount) {
            this.amount = amount;
        }

        public int getCurrent() {
            return current;
        }

        public void setCurrent(int current) {
            this.current = 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();
    }
}

1.4.2 创建进度对话框

ProgressMonitor的用法与ProgressBar的用法基本相似,只是ProgressMonitor可以直接创建一个进度对话框,它提供了下面的构造器完成对话框的构建:
ProgressMonitor(Component parentComponent, Object message, String note, int min, int max)
 parentComponent:对话框的父组件
 message:对话框的描述信息
 note:进度的提示信息
 min:进度条的最小值
 max:进度条的最大值
使用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(monitor.getMaximum());
//        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();

    }

    public static void main(String[] args) {
        new ProgressMonitorTest().init();
    }

    //定义一个线程任务,模拟耗时操作
    private class SimulaterActivity implements Runnable{
        //内存可见
        private volatile int current = 0;
        private int amount;

        public SimulaterActivity(int amount) {
            this.amount = amount;
        }

        public int getCurrent() {
            return current;
        }

        public void setCurrent(int current) {
            this.current = current;
        }


        public int getAmount() {
            return amount;
        }

        public void setAmount(int amount) {
            this.amount = amount;
        }

        @Override
        public void run() {
            //通过循环,不断的修改current的值,模拟任务完成量
            while(current<amount){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                current++;
            }
        }
    }

}

1.5 JList、JComboBox实现列表框

无论从哪个角度来看,JList和JComboBox都是极其相似的,它们都有一个列表框,总是JComboBox的列表框需要以下拉形式显示出来;JList和JComboBox都可以调用setRenderer()方法改变列表项的表示形式。甚至维护这两个组件的Model都是相似的,JList使用ListModel,JComboBox使用ComboBoxModel,而ComboBoxModel是ListModel的子类。

1.5.1简单列表框

使用JList或JComboBox实现简单列表框的步骤:
1.创建JList或JComboBox对象
JList(E[] listData):创建JList对象,把listData数组中的每项内容转换成一个列表项展示
JList(Vector<? extends E> listData):创建JList对象,把liatData数组中的每项内容转换成一个列表项展示
JComboBox(E[] items)
JComboBox(Vector items)
2.设置JList或JComboBox的外观行为

---------------------------------------JList--------------------------------------------------
addSelectionInterval(int anchor, int lead):在已经选中列表向的基础上,增加选中从anchor到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):设置选中项的前景色
setSelectionInterval(int anchor, int lead):设置从anchor到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完成。

import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.List;
import java.util.Vector;


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();

    JTextArea favorite = new JTextArea(4,40);


    //用一个字符串数组来创建一个JList对象
    JList<String> bookList ;
    JComboBox<String> bookSelector;

    public void init(){
        //组装视图

        //组装Jlist相关内容
        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);

        //对JList做设置
        bookList.setVisibleRowCount(4);
        bookList.setSelectionInterval(2,4);
        bookList.setSelectionBackground(Color.orange);
        bookList.setSelectionForeground(Color.gray);

        //处理条目选中事件
        bookList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                //获取当前选中的条目
                List<String> selectedValuesList = bookList.getSelectedValuesList();
                //把当前条目的内容设置到文本域中。
                favorite.setText("");
                for (String str : selectedValuesList) {
                    favorite.append(str+"\n");
                }
            }
        });

        Box bookListVBox = Box.createVerticalBox();
        bookListVBox.add(new JScrollPane(bookList));
        bookListVBox.add(layoutPanel);
        bookListVBox.add(selectModePanel);

        //组装JComboBox
        Vector<String> vector = new Vector<>();
        List<String> list = List.of("java自学宝典", "轻量级javaEE企业应用实战", "Android基础教程", "jQuery实战教程", "SpringBoot企业级开发");
        vector.addAll(list);

        bookSelector = new JComboBox<>(vector);

        bookSelector.setEditable(true);

        bookSelector.setMaximumRowCount(4);

        bookSelector.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                //获取当前已经选中的条目,把内容设置到文本域中
                Object selectedItem = bookSelector.getSelectedItem();
                favorite.setText(selectedItem.toString());
            }
        });

        //组装顶部的左右两部分
        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());

        bottomPanel.add(new JLabel("您最喜欢的图书:"),BorderLayout.NORTH);
        bottomPanel.add(favorite);

        //组装整体
        Box holeBox = Box.createVerticalBox();
        holeBox.add(topBox);
        holeBox.add(bottomPanel);

        jf.add(holeBox);


        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);

    }

    //封装方法,往layoutPanel中添加单选按钮
    public void addBtn2LayoutPanel(String name,int layoutType){
        //设置标题边框
        layoutPanel.setBorder(new TitledBorder(new EtchedBorder(),"确定选项布局"));

        //创建单选按钮
        JRadioButton button = new JRadioButton(name);

        layoutPanel.add(button);

        //让第一个按钮默认选中
        if (layoutGroup.getButtonCount()==0){
            button.setSelected(true);
        }

        layoutGroup.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                bookList.setLayoutOrientation(layoutType);
            }
        });
    }

    //封装方法,给selectModelPanel添加按钮
    public void addBtn2SelectModelPanel(String name,int selectionModel){
        //设置标题边框
        selectModePanel.setBorder(new TitledBorder(new EtchedBorder(),"确定选择模式"));

        //创建单选按钮
        JRadioButton button = new JRadioButton(name);

        selectModePanel.add(button);

        //让第一个按钮默认选中
        if (selectModeGroup.getButtonCount()==0){
            button.setSelected(true);
        }

        selectModeGroup.add(button);

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
               bookList.setSelectionMode(selectionModel);
            }
        });
    }


    public static void main(String[] args) {
        new ListTest().init();
    }
}

1.5.2 不强制存储列表项的ListMode和ComboBoxModel

与ProgressBar一样,JList和JComboBox也采用了MVC的设计模式,JList和JComboBox只负责外观的显示,而组件底层的状态数据则由对应的Model来维护。JList对应的Model是ListModel接口,JComboBox对应的Model是ComboBoxModel接口,其代码如下:

public interface ListModel<E>{
  int getSize();
  E getElementAt(int index);
  void addListDataListener(ListDataListener l);
  void removeListDataListener(ListDataListener l);
}
public interface ComboBoxModel<E> extends ListModel<E> {
  void setSelectedItem(Object anItem);
  Object getSelectedItem();
}

从上面接口看,ListModel不管JList里的所有列表项的存储形式,它甚至不强制存储所有的列表项,只要ListModel的实现类提供了getSize()和getElementAt()两个方法,JList就可以根据该ListModel对象来生成列表框。ComboBoxModel继承了ListModel,它添加了“选择项”的概念,选择项代表JComboBox显示区域内可见的列表项。
在使用JList和JComboBox时,除了可以使用jdk提供的Model实现类,程序员自己也可以根据需求,自己定义Model的实现类,实现对应的方法使用。

import javax.swing.*;
import java.math.BigDecimal;
import java.math.RoundingMode;

public class NumberListModel extends AbstractListModel<BigDecimal> {
    BigDecimal start;
    BigDecimal end;
    BigDecimal 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;
    }
}

import javax.swing.*;
import java.math.BigDecimal;
import java.math.RoundingMode;

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;
    }
}
import com.one.model.NumberComboBoxListModel;
import com.one.model.NumberListModel;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
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(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                List<BigDecimal> selectedValuesList = jList.getSelectedValuesList();
                jTextField.setText("");
                for (BigDecimal item : selectedValuesList) {
                    jTextField.setText(jTextField.getText()+item.toString()+",");
                }
            }
        });

        jComboBox.setMaximumRowCount(4);
        jComboBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent 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();
    }
}

1.5.3 强制存储列表项的DefaultListMode和DefaultComboBoxModel

前面只是介绍了如何创建了JList、JComboBox对象,当调用JList和JComboBox构造方法时时传入数组或Vector作为参数,这些数组元素或集合元素将会作为列表项。当使用JList或JComboBox时常常还需要动态地增加、删除列表项,例如JComboBox提供了下列方法完成增删操作:

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提供了如下几个方法添加、删除元素:

 add(int index, E element):在该ListModel的指定位置处插入指定元素
 addElement(E element):将指定元素添加到该ListModel的末尾
 insertElementAt(E element, int index):在该ListModel指定位置处插入指定元素
 E remove(int index):删除该ListModel中指定位置处的元素
 removeAllElements():删除该ListModel中所有元素,并将其的大小设为0
 removeElement(Object obj):删除该ListModel中第一个与参数匹配到元素
 removeElementAt(int index):删除该ListModel中指定索引处的元素
 removeRange(int fromIndex, int toIndex):删除该ListModel中指定范围内的所有元素
 set(int index, E element):将该ListModel指定索引处的元素替换成指定元素
 setElementAt(E element, int index):将该ListModel指定索引处的元素替换成指定元素
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
    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);

        //设置Jlist
        bookList.setVisibleRowCount(4);
        //设置单选
        bookList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        jf.add(new JScrollPane(bookList));

        //组装底部
        addBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String name = bookName.getText();
                if (!name.trim().equals("")){
                    model.addElement(name);
                }
            }
        });

        removeBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent 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();
    }

}

1.5.4 使用ListCellRenderer该变列表外观(列表外观渲染性)

前面程序中的JList和JComboBox采用的都是简单的字符串列表项,实际上,JList和JComboBox还可以支持图标列表项,如果在创建JList或JComboBox时传入图标数组,则创建的JList和JComboBox的列表项就是图标。如果希望列表项是更复杂的组件,例如希望QQ程序那样每个列表项既有图标又有字符串,此时需要使用ListCellRenderer接口的实现类对象,自定义每个条目组的渲染过程:

public interface ListCellRenderer<E>
{
    Component getListCellRendererComponent(
        JList<? extends E> list,//列表组件
        E value,//当前列表项的内容
        int index,//当前列表项的索引
        boolean isSelected,//当前列表项是否被选中
        boolean cellHasFocus);//当前列表项是否获取了焦点
}

import javax.swing.*;
import java.awt.*;

public class ListCellRendererTest {

    private JFrame mainWin = new JFrame("好友列表");

    private String[] friends = {
            "李清照",
            "苏格拉底",
            "李白",
            "弄玉",
            "傻虎"
    };

    //定义一个JList对象
    JList friendsList = new JList(friends);

    public void init() {
        //组装视图
        //给JList设置ListCellRenderer对象,指定列表项绘制的组件
        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("swing-app/img/list/"+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(),this.getWidth()/2 -imageWidth/2 ,10,null);

            //绘制昵称
            g.setColor(forceGround);
            g.setFont(new Font("StSong",Font.BOLD,18));
            g.drawString(this.name,this.getWidth()/2 - this.name.length()*20/2,imageHeight+30);

        }
    }


    public static void main(String[] args) {
        new ListCellRendererTest().init();
    }
    
}

1.6JTree、TreeModel实现树

1.6.1 创建数

按照结点(节点)是否包含子结点,可以把结点分为下面两类:
  普通结点:包含子结点的结点;
  叶子结点:没有子结点的结点;
按照结点是否具有唯一的父节点,可以把结点分为下面两类:
  根节点:没有父节点的结点,计算机中,一棵树只能有一个根节点
  普通结点:具有唯一的父节点的结点
如果某个结点是可见的,则该结点的父结点(包括直接的、间接的父结点)都必须位于展开状态,只要有任意一个父节点处于折叠状态,该结点是不可见的。
JTree的常用构造方法

方法名称方法功能
JTree(TreeModel newModel)使用指定的数据模型创建JTree对象,它默认显示根结点
JTree(TreeNode root)使用root作为根结点创建JTree对象,它默认显示根结点
JTree(TreeNode root, boolean asksAllowsChildren)使用root作为根结点创建JTree对象,他默认显示根结点。setALLowsChildren(false)显式设置某个结点不允许添加子结点时(以后也不会拥有子结点),该结点才会会被JTree当成叶子结点;如果该参数为false,则只要某个结点当时没有子结点(不管以后是否拥有子结点),该结点都会被JTree当成叶子结点

TreeNode继承体系及使用:
在这里插入图片描述
在构建目录时,可以先创建很多DefaultMutableTreeNode,并调用他们的add方法构建子父级结构,最后根据根结点创建一个JTree即可。

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;

public class SimpleJTree {
    JFrame jf = new JFrame("简单树");


    public void init(){

        //创建DefaultMutableTreeNode对象代表结点
        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对象
        JTree tree = new JTree(root);

        //把JTree放入到窗口中进行展示
        jf.add(tree);

        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);

    }


    public static void main(String[] args) {
        new SimpleJTree().init();
    }
}

JTree的其他外观设置方法:(JComponent的final方法)

方法名称方法功能
tree.putClientProperty(“JTree.lineStyle”, “None”)
tree.putClientProperty(“JTree.lineStyle”, "Horizontal"在这里插入代码片)

DefaultMutableTreeNode其他成员方法:

方法名称方法功能
Enumeration breadthFirstEnumeration()/preorderEnumeration()按广度优先的顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象
Enumeration depthFirstEnumeration()/postorderEnumeration()按深度优先顺序遍历以此结点为根的子树,并返回所有结点组成的枚举对象
DefaultMutableTreeNode getNextSibling()返回此结点的下一个兄弟结点
TreeNode getParent()返回此结点的父节点,如果没有,返回null
TreeNode[] getPath()返回从根结点到达此结点的所有结点组成的数组
DefaultMutableTreeNode getPreviousSibling()返回此结点的上一个兄弟结点
TreeNode getRoot()返回包含此结点的树到根结点
TreeNode getSharedAncestor(DefaultMutableTreeNode aNode)返回此结点和aNode最近的共同祖先
int getSiblingCount()返回此结点的兄弟结点数
boolean isLeaf()返回该结点是否是叶子结点
boolean isNodeAncestor(TreeNode anotherNode)判断anotherNode是否是当前结点的祖先结点(包括父结点)
boolean isNodeChild(TreeNode aNode)如果 aNode是此结点的子结点,则返回true
boolean isNodeDescendant(DefaultMutableTreeNode anotherNode)返回anotherNode是此结点的后代,包括是此结点本身、此结点的子结点或此结点子结点的后代,都返回true
boolean isNodeRelated(DefaultMutableTreeNode aNode)当aNode和当前结点位于同一棵树中时返回true
boolean isNodeSibling(TreeNode anotherNode)返回anotherNode是否是当前结点的兄弟结点
boolean isRoot()返回当前结点是否是根结点
Enumeration pathFromAncestorEnumeration(TreeNode ancestor)返回从指定祖先结点到当前结点的所有结点组成的枚举对象
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.util.Enumeration;

public class SimpleJTree2 {
    JFrame jf = new JFrame("简单树");


    public void init(){

        //创建DefaultMutableTreeNode对象代表结点
        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对象
        JTree tree = new JTree(root);

        //tree.putClientProperty( "JTree.lineStyle", "None"):设置结点之间没有连接线
        //tree.putClientProperty("JTree.lineStyle" , "Horizontal"):设置结点之间只有水平分割线

        //tree.putClientProperty( "JTree.lineStyle", "None");
        //tree.putClientProperty("JTree.lineStyle" , "Horizontal");



        //把JTree放入到窗口中进行展示
        jf.add(tree);

        //DefaultMutableTreeNode getNextSibling(): 返回此结点的下一个兄弟结点 。
        //TreeNode getParent(): 返回此结点的父结点 。 如果此结点没有父结点,则返回null 。
        //boolean isLeaf(): 返回该结点是否是叶子结点
        //boolean isRoot(): 返回当前结点是否是根结点 。

       /* DefaultMutableTreeNode nextNode = guangDong.getNextSibling();
        System.out.println(nextNode);*/
        /*TreeNode parent = guangDong.getParent();
        System.out.println(parent);*/
        /*System.out.println(guangDong.isLeaf());
        System.out.println(foShan.isLeaf());*/
        System.out.println(guangDong.isRoot());
        System.out.println(root.isRoot());


        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);

    }


    public static void main(String[] args) {
        new SimpleJTree2().init();
    }
}

1.6.2 编辑树结点

JTree生成的树默认是不可能的,不可以添加、删除结点,也不可以改变结点数据;如果想让某个JTree对象变成可编辑状态,则可调用JTree的setEditable(boolean b)方法,传入true即可把这棵树变成可编辑的树(可以添加、删除结点,也可以改变结点数据)
编辑树结点的步骤:
1.获取当前被选中的结点:
获取当前被选中的结点,会有两种方式:
 一:
  通过JTree对象的某些方法,例如TreePath getSelectionPath()等,得到一个TreePath对象,包括了从根结点到当前结点路径上的所有结点;
  调用TreePath对象的Object getLastPathComponent()方法,得到当前选中结点
 二、
  调用JTree对象的Object getLastSelectedPathComponent()方法获取当前被选中的结点
2.调用DefaultTreeModel数据模型有关增删改的一系列方法完成编辑,方法执行完后,重绘JTree。

import javax.swing.*;
import javax.swing.tree.*;
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() {

        //通过add()方法建立父子层级关系
        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(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //添加兄弟结点逻辑

                //1.获取当前选中的结点
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
                if (selectedNode == null) {
                    return;
                }

                //2.获取当前结点的父结点
                DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) selectedNode.getParent();
                if (parentNode == null) {
                    return;
                }

                //3.创建新结点
                DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");

                //4.把新结点通过父结点进行添加
                int index = parentNode.getIndex(selectedNode);
                model.insertNodeInto(newNode, parentNode, index);
                //parentNode.add(newNode);//会在最后面添加

                //5.显示新结点
                TreeNode[] pathToRoot = model.getPathToRoot(newNode);
                TreePath treePath = new TreePath(pathToRoot);
                tree.scrollPathToVisible(treePath);

                //6.重绘tree
                tree.updateUI();

            }
        });

        addChildBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //为选中结点添加子节点

                //1.获取选中结点
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
                if (selectedNode == null) {
                    return;
                }
                //2.创建新结点
                DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("新结点");

                //3.把新结点添加到当前结点中
                selectedNode.add(newNode);

                //4.显示新结点
                TreeNode[] pathToRoot = model.getPathToRoot(newNode);
                TreePath treePath = new TreePath(pathToRoot);
                tree.scrollPathToVisible(treePath);

                //5.重绘UI
                tree.updateUI();
            }
        });

        //处理删除
        deleteBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();

                if (selectedNode != null && selectedNode.getParent() != null) {
                    model.removeNodeFromParent(selectedNode);
                }

            }
        });

        //处理编辑
        editBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent 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();
    }
}

1.6.3 监听结点事件

为JTree添加监听监听器:
 1.addTreeExpansionListener(TreeExpansionListener tel):添加树结点展开/折叠事件的监听器
 2.addTreeSelectionListener(TreeSelectionListener tsl):添加树结点选择事件的监听器

修改JTree的选择模式:
JTree专门提供了一个TreeSelectionModel对象来保存该JTree选中状态的信息。也就是说,JTree组件背后隐藏了两个Model对象,其中TreeModel用于保存该JTree的所有结点数据,而TreeSelectionModel用于保存该JTree的所有选中状态的信息。
程序可以改变JTree的选择模式,但必须先获取该JTree对应的TreeSelectionModel对象,再调用该对象的setSelectionMode(int mode);方法设置该JTree的选择模式,其中model可以有以下三种取值:
 1.TreeSelectionModel.CONTIGUOUS_TREE_SELECTION:可以连续选中多个Treepath;
 2.TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION:该选项对于选择没有任何限制;
 3.TreeSelectionModel.SINGLE_TREE_SELECTION:每次只能选择一个TreePath。

import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

public class SelectJTree {
    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() {

        //通过add()方法建立父子层级关系
        guangdong.add(foshan);
        guangdong.add(shantou);
        guangxi.add(guilin);
        guangxi.add(nanning);
        root.add(guangdong);
        root.add(guangxi);

        tree = new JTree(root);

        //TODO 设置选择模式
        TreeSelectionModel selectionModel = tree.getSelectionModel();
        selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

        //TODO 设置监听器
        tree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent 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 SelectJTree().init();
    }
}

1.6.4 使用DefaultTreeCellRenderer改变结点外观

JTree默认的外观是比较单一的,它提供了机制改变结点外观的方式:
 1.使用DefaultTreeCellRenderer直接改变结点的外观,这种方式可以改变这棵树所有结点的字体、颜色和图标。
 2.为JTree指定DefaultTreeCellRenderer的扩展类对象作为JTree的结点绘制器,该绘制器负责为不同结点使用不同的字体、颜色和图标等。通常使用这种方式来改变结点的外观。
 3.为JTree指定一个实现TreeCellRenderer接口的结点绘制器,该绘制器可以为不同的结点自由绘制任意内容,这是最复杂但最灵活的结点绘制器。
第一种方式最简单,但灵活性差,因为他会改变整棵树的所有结点外观。在这种情况下,JTree的所有结点依然使用相同的图标,相当于整体替换了JTree中结点的所有默认图标。用户指定的点的节点图标未必就比JTree默认图标美观。

DefaultTreeCellRenderer提供了如下几个方法来修改结点的美观:

方法
setBackgroundNonSelectionColor(Color newColor):设置用于非选定节点的背景颜色
setBackgroundSelectionColor(Color newColor):设置节点在选中状态下的背景颜色
setBorderSelectionColor(Color newColor):设置选中状态下结点的边框颜色
setClosedIcon(Icon newIcon):设置处于折叠状态下非叶子节点的图标
setFont(Font font):设置节点文本的字体
setLeafIcon(Icon newIcon:设置叶子节点的图标
setOpenIcon(Icon newIcon):设置处于展开状态下非叶子节点的图标
setTextNonSelectionColor(Color newColor):设置绘制非选中状态下节点文本的颜色
setTextSelectionColor(Color newColor):设置绘制选中状态下节点文本的颜色
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
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(){
        //通过add()方法建立父子层级关系
        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("swing-app\\img\\tree\\close.gif"));
        //设置结点文本的字体
        renderer.setFont(new Font("StSong",Font.BOLD,16));
        //设置叶子结点图标
        renderer.setLeafIcon(new ImageIcon("swing-app\\img\\tree\\leaf.png"));
        //设置处于展开状态下非叶子结点图标
        renderer.setOpenIcon(new ImageIcon("swing-app\\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();
    }

}

1.6.5 扩展DefaultTreeCellRenderer改变结点外观

DefaultTreeCellRenderer实现类实现了TreeCellRenderer接口,该接口里只有一个用于绘制节点内容的方法;getTreeCellRendererComponent(),该方法负责绘制JTree节点。学习JList的时候,如果要绘制JList的列表项外观的内容,需要实现ListCellRenderer接口,通过重写Component getTreeCellRenderCompoent()方法返回一个Component对象,该对象就是JTree的节点组件。两者之间非常类似。
DefaultTreeCellRenderer类继承了JLabel,实现getTreeCellRendererComponent()方法时返回this,即返回一个特殊的JLabel对象。如果需要根据节点内容来改变结点的外观,则可以再次扩展DefaultTreeCellRenderer类,并再次重写它提供的getTreeCellRendererComponent()方法。

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import java.awt.*;

public class ExtendsDefaultCellTreeRenderer {

    JFrame jf = new JFrame("根据结点类型定义图标");

    JTree tree;

    //初始化5个图标
    ImageIcon rootIcon = new ImageIcon("swing-app\\img\\tree\\root.gif");
    ImageIcon databaseIcon = new ImageIcon("swing-app\\img\\tree\\database.gif");
    ImageIcon tableIcon = new ImageIcon("swing-app\\img\\tree\\table.gif");
    ImageIcon columnIcon = new ImageIcon("swing-app\\img\\tree\\column.gif");
    ImageIcon indexIcon = new ImageIcon("swing-app\\img\\tree\\index.gif");

    //定义一个NodeData类,用于封装结点数据
    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(){
        //通过结点的add方法,建立结点的父子关系

        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);

        //TODO 通过扩展DefaultTreeCellRenderer修改外观
        tree.setCellRenderer(new MyRenderer());


        jf.add(new JScrollPane(tree));
        jf.pack();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);

    }


    //自定义类,继承DefaultTreeCellRenderer,完成结点的绘制
    private class MyRenderer extends DefaultTreeCellRenderer{

        //重写方法


        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
            //当前类间接的继承了JLabel这个组件类,展示一张图片和一些配套的文字
            //Object value这个参数,代表的就是即将要绘制的结点

            //获取当前结点
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;

            //获取到当前即将绘制的结点的名称和图标
            NodeData nodeData = (NodeData) node.getUserObject();

            //通过setText方法和setIcon方法完成设置
            this.setText(nodeData.name);
            this.setIcon(nodeData.icon);
            return this;
        }
    }

    public static void main(String[] args) {
        new ExtendsDefaultCellTreeRenderer().init();
    }

}

1.6.6 实现DefaultTreeCellRenderer改变结点外观

这种改变结点外观的方式最灵活,程序实现TreeCellRenderer接口时同样需要实现getTreeCellRendererComponent()方法,该方法可以返回任意类型的组件,该组件将作为JTree的结点。通过这种方式可以最大程度的改变结点的外观。

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);

        //TODO 设置结点绘制器
        MyRenderer renderer = new MyRenderer();
        tree.setCellRenderer(renderer);

        jf.add(new JScrollPane(tree));
        jf.pack();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);

    }

    //自定义类,实现TreeCellRenderer接口,绘制组件
    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("swing\\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;
        }

        //通过重写getPreferenceSize方法,指定当前Jpanel组件的大小

        @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();
    }
}

1.7 JTable、TableModel实现表格

表格也是GUI程序中常用的组件。表格是一个由多行、多列组成的二维显示区。Swing的JTable以及相关类提供了这种表格支持,通过使用JTable以及支持相关类,程序既可以使用简单的代码创建初表格来显示二维数据,也可以开发出功能丰富的表格,还可以为表格定制各种显示比例、编辑特性。

1.7.1 创建简单表格

使用JTable创建简单表格的步骤:
 1.创建一个以为数组,存储表格中每一列的标题
 2.创建一个二维数组,存储表格中的每一行数据,其中二维数组内部的每一个一维数组,代表表格中的一行数据
 根据第一步和第二步创建的一维和二维数组,创建JTable对象
 把JTable添加到其他容器中显示

import javax.swing.*;

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();
    }
}

JTable调整列宽:
JTable使用TableColumn来表示表格中的每一列,JTable中表格列的所有属性,如最佳宽度、是否可调整宽度、最小和最大宽度等都保存在该TableColumn中。
 1setMaxWidth(int maxWidth):设置该列的最大宽度。如果指定的maxWidth小于该列的最小宽度,则maxWidth被设置成最小宽度。
 2.setMinWidth(int minWidth):设置该列的最小宽度。
 3.setPreferredWidth(int preferredWidth):设该列的最佳宽度。
JTable调整表格选择模式:
与JList类似的是,JTable使用了一个ListSelectionModel表示该表格的选择状态,程序可以通过ListSelectionModel.setSelectionMode(int mode)控制JTable的选择模式。JTable的选择模式有如下三种:
 1.ListSelectionModel.SINGLE_SELECTION : 只能选择单行
 2.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION :没有任何限制
 3.ListSelectionModel.SINGLE_INTERVAL_SELECTION : 选择单个连续区域

import javax.swing.*;
import javax.swing.table.TableColumn;

public class AdjustingWidth {

    JFrame jf = new JFrame("调整表格");

    //定义一个一维数组,作为列标题
    Object[] titles = {"姓名","年龄","性别"};

    //定义一个二维数组,作为表格行数据
    Object[][] data = {

            new Object[]{"李清照",29,"女"},
            new Object[]{"苏格拉底",56,"男"},
            new Object[]{"李白",35,"男"},
            new Object[]{"弄玉",18,"女"},
            new Object[]{"虎头",2,"男"},

    };

    public void init(){

        //创建JTable对象
        JTable table  = new JTable(data,titles);

        //TODO 设置选择模式
        table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);//默认,没有限制
        //table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);//只能选择单个连续范围
        //table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);//只能选择单行
        //TODO 设置列宽
        TableColumn column_1 = table.getColumn(titles[0]);
        column_1.setMinWidth(40);//最小为40
        TableColumn column_3 = table.getColumn(titles[2]);
        column_3.setMaxWidth(50);//最大只能50
        jf.add(new JScrollPane(table));
        jf.pack();
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);

    }

    public static void main(String[] args) {
        new AdjustingWidth().init();
    }

}

1.7.2 TableModel

与JList、JTree类似的是,JTable采用了TableModel来保存表格中的所有状态数据;与ListModel类似的是,TableModel也不强制保存该表格显示的数据。虽然在前面程序中看到的是直接利用一个二维数组来创建JTable对象,但也可以通过TableModel对象来创建表格。
自定义TableModel步骤:
 1.自定义类,继承AbstractTableModel抽象类,重写下面几个方法:

int getColumnCount():返回表格列的数量
int getRowCount():返回表格行的数量
Object getValueAt(int rowIndex, int columnIndex):返回rowIndex行,columnIndex列的单元格的值
String getColumnName(int columnIndex) :返回columnIndex列的名称
boolean isCellEditable(int rowIndex, int columnIndex):设置rowIndex行,columnIndex列的单元格的值是否可编辑

 2.创建自定义类对象,根据该对象,创建JTable对象。

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
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(dataV,titlesV);
        JTable jTable = new JTable(myTableModel);

        jf.add(new JScrollPane(jTable));

        JButton btn = new JButton("获取选中行数据");
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent 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) {//默认是true
            return false;
        }
    }



    public static void main(String[] args) {
        new TableModelTest().init();
    }

}

不仅用户可以扩展AbstractTableModel抽象类,Swing本身也为AstractTableModel提供了一个DefaultTableModel实现类,程序可以通过使用DefaultTableModel实现类来创建JTable对象。通过DefaultTableModel对象创建JTable对象后,就可以调用它提供的方法来添加数据行、插入数据行、删除数据行和移动数据行。DefaultTableModel提供了如下几个方法来控制数据行操作:

方法名称方法功能
addColumn(Object columnName)/addColumn(Object columnName, Object[] columnData)添加一列
addRow(Object[] rowData)添加一行
insertRow(int row, Object[] rowData)指定位置插入一行
removeRow(int row)删除一行
moveRow(int start, int end, int to)移动指定范围内的数据行
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

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创建JTable
        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(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //需要给表格添加一行
                model.addRow(new Object[]{"柳岩",18,"女"});
            }
        });

        deleteRow.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int selectedRow = jTable.getSelectedRow();
                model.removeRow(selectedRow);
            }
        });



        addColum.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent 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();
    }

}

1.8 AWT编程

AWT编程基础

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值