swing图形用户界面设计

swing

Swing概述:

Swing 是100%纯Java实现的,不依赖于本地平台的GUI,因此可以在所有平台上都保持相同的界面外观。独立于本地平台的Swing组件被称之为轻量级组件,而依赖本地平台的AWT组件被称之为重量级组件。

Swing优势:

  1. Swing组件不再依赖本地平台的GUI,不需要采用各种平台的交集,因此Swing提供了大量的图形界面组件,远远超出AWT所提供的图形界面组件
  2. Swing组件在各种平台上运行是都可以保证相同的图形界面外观
  3. Swing组件 让Java图形界面程序真正的实现了“Write Once,Run Anywhere”

Swing的特征:

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

Swing基本组件的用法

awt继承体系图:
在这里插入图片描述

Swing组件继承体系图:

在这里插入图片描述

绝大部分Swing组件继承了Container类,使用Swing组件都可以当成容器使用(JFrame继承了Frame)

Swing组件和AWT组件的对应:

  1. JcomboBox:对应于AWT里面的Choice组件,但是比Choice组件功能更加丰富。(下拉选择框)
  2. JFileBhooser:对应于AWT中的FileDialog组件。(文件选择框)
  3. JScorllBar:对应于AWT里面的Scrollbar组件,注意:字母大小写(滑动条)
  4. JCheckBox:对应于AWT中的Checkbox组件,注意:字母大小写(复选框)
  5. JCheckBoxMenuItem:对应于AWT中的CheckboxMenuItem组件,注意:字母大小写()

Swing组件按照功能划分:

  1. 顶级容器:JFrame,JApplet,JDialog和JWindow.
  2. 中间容器:JPanel,JScrollPane,JSplitPane,JToolBar 等等;
  3. 特殊容器:在图形用户界面上有特殊作用的容器,JIntemalFrame,JRootPane,JLayeredPane和JDestopPane等等
  4. 基本组件:实现人机交互的组件:如JButton,JComboBox,JList,JMenu,JSlider等等
  5. 不可以编辑信息的显示组件:向用户显示不可以编辑的信息:JLable,JProgressBar和JtoolTip等等
  6. 可以编辑信息的显示组件:JTable,JTextArea和JTextField等
  7. 特殊对话框组件:可以直接产生特殊对话框的组件,JColorChooser和JFileChooser等等
  8. Radio单选
Swing边框

给组件设置边框,使得界面的层次感更加明显,swing中提供了Border对象来表示一个边框

Border继承体系:

在这里插入图片描述

特殊的Border:

  1. TitleBoirder:它的作用并不是直接为其他组件添加边框,而是为其他边框设置标题,创建该类的对象时,需要传入一个其他类的Border对象
  2. CompoundBorder:用来组合其他两个边框,创建该类的对象时,需要传入其他两个Border对象,一个作为内边框,一个作为外边框

给组件设置边框的步骤:

  1. 使用BorderFactory或者XXXBorder创建Border的实例对象
  2. 调用Swing组件的setBorder(Border b )方法为组件设置边框

案例:

package com.swing.xm.component;

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

/**
 * 设置边框样式
 */
public class Demo2 {

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

    public void init() {
        //网格布局
        frame.setLayout(new GridLayout(2, 4));
        //在不同的网格中设置边框和内容
        //创建 BevelBorder
        Border border = BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.RED, Color.BLUE, Color.gray, Color.BLACK);
        frame.add(getJPanelWithBorder(border, "BevelBorder"));
        //创建LineBorder
        Border border2 = BorderFactory.createLineBorder(Color.RED, 10);
        frame.add(getJPanelWithBorder(border2, "LineBorder"));
        //EmptyBorder 是MatteBorder的父类
        Border border3 = BorderFactory.createEmptyBorder(5, 5, 5, 5);
        frame.add(getJPanelWithBorder(border3, "EmptyBorder"));
        //创建EtchedBorder
        Border border4 = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.RED, Color.BLUE);
        frame.add(getJPanelWithBorder(border4, "EtchedBorder"));
        //创建TitleBorder,为边框设计标题
        TitledBorder titledBorder = new TitledBorder(new LineBorder(Color.ORANGE, 5), "测试边框标题", TitledBorder.LEFT, TitledBorder.BOTTOM, new Font("StSong", Font.BOLD, 18), Color.RED);
        frame.add(getJPanelWithBorder(titledBorder, "TitleBorder"));
        //创建MatteBorder   是EmptyBorder的子类
        MatteBorder matteBorder = new MatteBorder(5, 5, 5, 10, Color.RED);
        frame.add(getJPanelWithBorder(matteBorder, "MatteBorder"));
        //创建CompoundBorder   第一个参数是外边框,第二个是内边框
        CompoundBorder compoundBorder = new CompoundBorder(titledBorder, matteBorder);
        frame.add(getJPanelWithBorder(compoundBorder, "CompoundBorder"));


        //设置窗口大小
        frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

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

JTooBar

Swing提供JToolBar类来创建工具条,并且可以向JToolBar中添加多个工具按钮。

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

add(Action a)方法:

Acttion 接口时ActionListener的一个子接口,他就代表一个时间监听器,而这里的add方法是在给工具条添加一个工具按钮,为什么传递的是一个事件监听器呢?

首先要明确的是不管是菜单项还是工具按钮,最终都是需要点击来完成一些动作,所以JToolBar 和JMenu都提供了更加便捷的添加子组件的方法add(Action a)在这个方法的内部会做如下几个动作:

  1. 创建一个适用于该容器的组件(例如:在工具栏中创建一个工具按钮);
  2. 从Action 对象中霍格对应的属性来设置该组件(通过Icon来设置按钮图标);
  3. 把Action监听器注册到刚才创建的组件上;

案例:

package com.swing.xm.component;

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

/**
 * @author Ilff
 * 2022/7/27
 * 16:41
 */
public class Demo3 {
    JFrame jFrame = new JFrame("测试工具条");
    JTextArea jTextArea = new JTextArea(6, 40);
    JToolBar jToolBar = new JToolBar("工具条", SwingConstants.HORIZONTAL);


    Action pre = new AbstractAction("上一曲", new ImageIcon("B\\img\\component\\ok1.jpg")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jTextArea.append("上一曲\n");
        }
    };
    Action pause = new AbstractAction("暂停播放", new ImageIcon("B\\img\\component\\ok1.jpg")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jTextArea.append("暂停\n");
        }
    };
    Action next = new AbstractAction("下一曲", new ImageIcon("B\\img\\component\\ok1.jpg")) {
        @Override
        public void actionPerformed(ActionEvent e) {
            jTextArea.append("下一曲\n");
        }
    };
   //JButton(Action a) 
   //创建一个按钮,其中的属性取自提供的 Action 。 
    JButton jButton1 = new JButton(pre);
    JButton jButton2 = new JButton(pause);
    JButton jButton3 = new JButton(next);

    public void init() {

//jToolBar.add(pre)会产生一个JButton对象,属性取自提供的Action,并加入到jToolBar中
/*        jToolBar.add(pre);
        jToolBar.addSeparator();
        jToolBar.add(pause);
        jToolBar.addSeparator();
        jToolBar.add(next);*/
        jToolBar.add(jButton1);
        jToolBar.addSeparator();
        jToolBar.add(jButton2);
        jToolBar.addSeparator();
        jToolBar.add(jButton3);
        jFrame.add(jToolBar, BorderLayout.NORTH);
        //添加进度条
        JScrollPane jScrollPane = new JScrollPane(jTextArea);
        jFrame.add(jScrollPane);
        jToolBar.setFloatable(true);
        jFrame.pack();
        jFrame.setVisible(true);

        jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }


    public static void main(String[] args) {
        new Demo3().init();
    }
}
JColorChooser和FileChooser

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

public start Color showDialog(Component component,String title,Color initialColor)  

component:指定当前对话框的父组件    title:当前对话框的名称    initialColor:指定默认选中的颜色

return  用户选中的颜色

代码展示:

package com.swing.xm.component;

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

/**
 * JColorChooser:测试演示选择器
 * @author IlffQ
 * 2022/7/27
 * 22:17
 */
public class JColorChooser {
    JFrame jFrame=new JFrame("测试颜色选择器");
    JTextArea jTextArea=new JTextArea("中国梦,我的梦!",5,40);
    JScrollPane jScrollPane=new JScrollPane(jTextArea);
    public void init(){

        JButton jButton = new JButton(new AbstractAction("改变颜色") {
            @Override
            public void actionPerformed(ActionEvent e) {
                Color color = javax.swing.JColorChooser.showDialog(jFrame, "颜色选择", Color.white);
                jTextArea.setBackground(color);
            }
        });

        jFrame.add(jScrollPane);
        jFrame.add(jButton,BorderLayout.SOUTH);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(jFrame.EXIT_ON_CLOSE);
    }


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

JFileChooser的功能和AWT中的FileDialog基本相似,也用于生成"打开文件",”保存文件“的对话框。于FileDialog不同的是,JFileChooser不用依赖本地的GUI ,它是纯Java实现的,在所有的平台上具有完全相同的行为,可以在所有平台上具有相同的风格。

创建JFileChooser对象

JFileChooser chooser=new JFileChooser(“指定的路径”)

打开对话框:

showOpenDialog(component parent):打开文件加载对话框,并指定父组件

showSaveDialog(component parent ):打开文件保存对话框,并指定父组件

获取用户选择结果:

File getSelectedFile()获取用户选择的文件

package com.swing.xm.component;


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;

/**
 * JFileChooser:
 * 测试文件选择器
 *
 * @author IlffQ
 * 2022/7/27
 */
public class JFileChooser {
    JFrame jFrame = new JFrame("测试文件选择器");
    JMenuBar jMenuBar = new JMenuBar();
    JMenu jMenu = new JMenu("文件");
    BufferedImage image;
    JMenuItem open = new JMenuItem(new AbstractAction("打开") {
        @Override
        public void actionPerformed(ActionEvent e) {
            javax.swing.JFileChooser chooser = new javax.swing.JFileChooser(".");
            chooser.showOpenDialog(jFrame);
//            chooser.setSelectedFile(new File("C:\\Users\\86182\\Pictures\\Camera Roll\\Idea背景\\3.jpg"));
            File selectedFile = chooser.getSelectedFile();
            try {
                image = ImageIO.read(selectedFile);
                myCanvas.repaint();
            } catch (IOException ex) {
                ex.printStackTrace();
            }

        }
    });
    JMenuItem save = new JMenuItem(new AbstractAction("另存为") {
        @Override
        public void actionPerformed(ActionEvent e) {
            javax.swing.JFileChooser chooser = new javax.swing.JFileChooser("C:\\Users\\86182\\Pictures\\Camera Roll\\Idea背景");
            chooser.showSaveDialog(jFrame);
            //保存路径:
            File selectedFile = chooser.getSelectedFile();
            try {
                ImageIO.write(image, "jpeg", selectedFile);
                myCanvas.repaint();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    });

    class MyCanvas extends JPanel {
        @Override
        public void print(Graphics g) {
            g.drawImage(image, 0, 0, null);
        }
    }

    MyCanvas myCanvas = new MyCanvas();

    public void init() {
        jMenu.add(open);
        jMenu.add(save);
        jMenuBar.add(jMenu);
        jFrame.setJMenuBar(jMenuBar);
        myCanvas.setPreferredSize(new Dimension(1920, 1080));
        jFrame.add(myCanvas);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

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

JOptionPanel

通过JOptionPanel可以非常方便的创建一些简单的对话框,Swing为这些对话框添加了相应的组件,JOptionPane提过了如下4种方法创建对话框。

方法名称方法功能
showMessageDialog/showInternalMessageDialog消息对话框,告知用户某事已经发生,用户只能点击确定按钮,类似于JavaScript的alert函数。
showConfirmDialog/showInternalConfirmDialog确认对话框,向用户确认某个问题,用户可以选择yes.no.cancel等等选项,类似于JavaScript的comfrim函数,该方法返回用户点击了哪个按钮。
showInputDialog/showInternalInputDialog输入对话框,提示要求输入某些信息,类似于JavaScript的prompt函数,该方法用户返回用户输入的字符串
showOptionDialog/showInternalOptionDialog自定义选项对话框,允许使用自定义选项,可以取代showConfirmDialog所产生的对话框只是比较复杂。

上述方法都有比较多的重载形式,下面是其中一种最全面的形式,参数如下:

showxxxDialog(Component parentComponent,Bobject message,String title,int optionType,int messageType,Icon icon,Object[] iptions,Object initialVale )
    其中:
    parentComponet:当前对话框的父组件
    message:对话框上的提示信息,可以是字符串,组件,图片等等
    title:当前对话框的标题
    optionType:当前对话框上显示的按钮类型:DEFAULT_OPTION,YES_NO_OPTION,YES_NO_CANCEL_OPTION,OK_CANCEL_OPTION
    messageType:当前对话框的类型:ERROR_MESSAGE,INFORMATION_MESSAGE(提示信息),WARNING_MESSAGE,QUESTION_MESSAGE,PLAIN_MESSAGE(普通信息)
    icon: 当前对话框左上角的图标
    options:自定义下拉列表的选项
    initialValue:自定义选项中的默认选中项

当前用户和对话框交互结束后,不同类型的对话框返回值如下:

  • showMessageDialog :没有返回值
  • showInputDialog: 返回用户输入或选择的字符串
  • showConfirmDialog:返回一个整数代表用户选择的选项
  • showOPtionDialog:返回一个整数代表用户选择的选项,如果用户选择第一项,则返回0,如果选择第二项,则返回1…

对于showConfirmDialog所产生的对话框,有如下几个返回值:

  • YES OPTION:用户点击了 yes 按钮后返回。
  • NO OPTION :用户点击了 no 按钮后返回。
  • CANCEL OPTION :用户点击了 cancel 按钮后返回。
  • OK OPTION :用户点击了 ok 按钮后返回。
  • CLOSED OPTION :用户点击了 右上角 X 按钮后返回。

四种对话框演示:

package com.swing.xm.component;

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

/**
 * JOptionPane:
 *
 * @author IlffQ
 * 2022/8/3
 */
public class JOptionPane {
    JFrame jFrame = new JFrame("测试各种对话框");
    JTextArea jTextArea = new JTextArea(6, 40);

    public void init1() {
        //showMessageDialog
        JButton jButton = new JButton(new AbstractAction("弹出消息对话框") {
            @Override
            public void actionPerformed(ActionEvent e) {
                String text = jTextArea.getText();
                javax.swing.JOptionPane.showMessageDialog(jFrame, text, "消息对话框",
                        javax.swing.JOptionPane.ERROR_MESSAGE, new ImageIcon("B\\img\\component\\ok1.png"));
            }
        });

        jFrame.add(jTextArea);
        jFrame.add(jButton, BorderLayout.SOUTH);
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

    public void init2() {
        //showConfirmDialog
        JButton jButton = new JButton(new AbstractAction("弹出确认对话框") {
            @Override
            public void actionPerformed(ActionEvent e) {
                String text = jTextArea.getText();
                int i = javax.swing.JOptionPane.showConfirmDialog(jFrame, text, "确认对话框", javax.swing.JOptionPane.YES_NO_CANCEL_OPTION
                        , javax.swing.JOptionPane.QUESTION_MESSAGE, new ImageIcon("B\\img\\component\\ok1.png"));
                if (i == javax.swing.JOptionPane.YES_OPTION)
                    jTextArea.append("用户点击了 确认 按钮\n");
                if (i == javax.swing.JOptionPane.CLOSED_OPTION)
                    jTextArea.append("用户点击了 关闭 按钮\n");
            }
        });

        jFrame.add(jTextArea);
        jFrame.add(jButton, BorderLayout.SOUTH);
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void init3() {
        //showInputDialog
        JButton jButton = new JButton(new AbstractAction("弹出输入对话框") {
            @Override
            public void actionPerformed(ActionEvent e) {
                String inputDialog = javax.swing.JOptionPane.showInputDialog(jFrame, "输入号码:",
                        "输入对话框", javax.swing.JOptionPane.INFORMATION_MESSAGE);
                jTextArea.append(inputDialog);
            }
        });

        jFrame.add(jTextArea);
        jFrame.add(jButton, BorderLayout.SOUTH);
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void init4() {
        //showJOptionDialog
        JButton jButton = new JButton(new AbstractAction("弹出选项对话框") {
            @Override
            public void actionPerformed(ActionEvent e) {
                int showOptionDialog = javax.swing.JOptionPane.showOptionDialog(jFrame, "选择意大利炮的规格", "选项对话框",
                        javax.swing.JOptionPane.OK_CANCEL_OPTION, javax.swing.JOptionPane.INFORMATION_MESSAGE,
                        null, new String[]{"大号", "中号", "小号"}, "中号");
                switch (showOptionDialog) {
                    case 0:
                        jTextArea.setText("用户选择大号\n");//不可以追加,append可以追加
                        break;
                    case 1:
                        jTextArea.setText("用户选择中号\n");
                        break;
                    case 2:
                        jTextArea.setText("用户选择小号\n");
                        break;
                }

            }
        });
        jFrame.add(jTextArea);
        jFrame.add(jButton, BorderLayout.SOUTH);
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
      /*new JOptionPane().init1();
        new JOptionPane().init2();
        new JOptionPane().init3();
        new JOptionPane().init4();*/
    }
}

swing特殊容器

JSplitPane 拆分式窗口(面板)

JSplitPane用于创建一个分割面板,它可以将一个组件(容器)分割成两部分,并提供一个分割条,用户可以拖动分割条来调整两个部分的大小。

使用步骤:

  1. 创建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):设置指定位置的组件

效果演示:

package com.swing.xm.special_container;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;

/**
 * JSplitPaneTest:
 *
 * @author IlffQ
 * 2022/8/3
 */
public class JSplitPaneTest {

    Book[] books = new Book[]{
            new Book("恋爱教程", new ImageIcon("B\\img\\component\\3.jpg"), "教你如何谈恋爱\n,为祖国培养人才,不对,是海王"),
            new Book("编程练习", new ImageIcon("B\\img\\component\\ok.png"), "教你如何挣Money\n,以后不至于饿死"),
            new Book("数学素养", new ImageIcon("B\\img\\component\\ok1.png"), "教你如何学好数学")
    };
    JFrame jFrame = new JFrame("测试JSplitPane");
    /**
     * 列表框中的内容为数组中各个元素的toString内容
     */
    JList<Book> jList = new JList<>(books);

    JLabel jLabel = new JLabel();

    JTextArea jTextArea = new JTextArea(4, 20);

    public void init() {
        //设置组件大小
        jList.setPreferredSize(new Dimension(150, 400));
        jLabel.setPreferredSize(new Dimension(220, 270));
        jTextArea.setPreferredSize(new Dimension(220, 130));

        JSplitPane left = new JSplitPane(JSplitPane.VERTICAL_SPLIT, jLabel, new JScrollPane(jTextArea));
        left.setContinuousLayout(true);
        left.setOneTouchExpandable(true);
        JSplitPane total = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, jList);
        total.setContinuousLayout(true);
        total.setOneTouchExpandable(true);
        total.setDividerSize(20);
        jList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                Book book = jList.getSelectedValue();
                jLabel.setIcon(book.getIcon());
                jTextArea.setText(book.getDesc());

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

    }

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


    }
}

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 Icon getIcon() {
        return icon;
    }

    public String getDesc() {
        return desc;
    }

    /**
     * @return 列表框中的内容
     */
    @Override
    public String toString() {
        return this.name;
    }
}
JTabledPane 选项卡式窗口(面板)

JTablePane可以方便的在窗口上放置多个标签页,每个标签页相当于获得了一个与外部容器具有相同大小的组件摆放区域,通过这种方式,就可以在一个容器中放置更多的组件。

1.创建JTablePane对象

JTabbedPane(int tabplacement,int tabLayoutPolicy)
    tabPlacement:指定标签的放置位置,可以选择SwingConstants中的四个常量:TOP,LEFT,BOTTOM,RIGHT
    tabLayoutPolicy:指定当前窗口不能容纳标签页的布局策略,可以选择JTabbedPane.WRAP_TAB_LAYOUT和JTabbedPane.SCROLL_TAB_LAYOUT 

2.通过JTablePane对象坐标进行增删改查

add("ILFFQ")
//TODO 补充信息!
addTab(String title,Icon icon,Component component, String tip):添加标签
    title:标签的名称
    icon:标签的图标
    component:标签对应的组件
    tip:鼠标放到标签上的提示信息
        
insertTab(Sring title,Icon icon,Component component,String tip,int index):插入标签
    title:标签的名称
    icon:标签的图标
    component:标签对应的组件
    tip:鼠标放到标签上的提示信息
    index:在哪个索引处插入标签页
        
setComponentAt(int index,Component component):修改标签页对应的组件
    index:修改哪一个索引处的标签
    component:标签对应的组件
        
removeTabAt(int index):删除索引处的标签

3.设置当前显示页面的标签页

setSelectedIndex(int index):设置哪个索引处的标签被选中

4.设置JTabbedPane的其他属性

setDisabeInconAt(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(ChangeeListener l)

效果演示

package com.swing.xm.special_container;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
 * JTablePane:
 *
 * @author IlffQ
 * 2022/8/4
 */
public class JTabbedPaneTest {

    JFrame jFrame = new JFrame("测试选项卡面板");
    JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT);

    public void init() {
        //标签部分
        jTabbedPane.addTab("用户管理", new ImageIcon("B\\img\\component\\ok1.png"),
                new JList<String>(new String[]{
                        "用户1",
                        "用户2",
                        "用户3"}), "真不错");
        jTabbedPane.addTab("商品管理", new ImageIcon("B\\img\\component\\ok1.png"),
                new JList<String>(new String[]{
                        "商品1",
                        "商品2",
                        "商品3"}), "真不错");
        jTabbedPane.addTab("订单管理", new ImageIcon("B\\img\\component\\ok1.png"),
                new JList<String>(new String[]{
                        "订单1",
                        "订单2",
                        "订单3"}), "真不错");


        //设置部分
        jTabbedPane.setEnabledAt(0, false);
        jTabbedPane.setSelectedIndex(1);  // 这里很坑,不要写在监听的后边!!!!
        jFrame.setBounds(0, 0, 500, 300);

        //监听部分
        jTabbedPane.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                int selectedIndex = jTabbedPane.getSelectedIndex();
                JOptionPane.showMessageDialog(jTabbedPane, "你选择了" + (selectedIndex + 1) + "" + "个对话框");

            }
        });

        jFrame.add(jTabbedPane);
        jFrame.setResizable(false);
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        new JTabbedPaneTest().init();
    }
}

swing JProgressBar 进度条

JProgressBar

使用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 getPerentComplete():获取进度条的完成百分比
String getString():返回进度字符串的当前值

案例 子线程模拟进度

package com.swing.xm.jprogress_bar;

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

/**
 * JProgressBarTest:
 *
 * @author IlffQ
 * 2022/8/4
 */
public class JProgressBarTest {
    JFrame jFrame = new JFrame("测试进度条");
    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");
    JProgressBar bar = new JProgressBar(SwingConstants.HORIZONTAL, 0, 100);

    public void init() {
        // 处理监听
        indeterminate.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean selected = indeterminate.isSelected();
                bar.setIndeterminate(selected);
                bar.setStringPainted(!selected);
            }
        });
        noBorder.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean selected = noBorder.isSelected();
                bar.setBorderPainted(!selected);
            }
        });

        //设置属性(初始)
        //1.绘制百分百比
        bar.setStringPainted(true);
        //2.绘制边框
        bar.setBorderPainted(true);

        //组装视图
        Box box = Box.createVerticalBox();
        box.add(indeterminate);
        box.add(noBorder);
        Box box1 = Box.createHorizontalBox();
        box1.add(box);
        box1.add(bar);
        jFrame.add(box1);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //模拟进度
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {

                    bar.setValue(i + 1);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public static void main(String[] args) {
        new JProgressBarTest().init();
        System.out.println("你好");
    }
}

JProgressBar_Model

swing中有很多的组件的界面与数据都采用了MVC的设计思想:

Model:数据模型,封装数据 ----->Contral:控制器,吧Model中的数据交给View展示 ----->View:视图,展示数据的组件

swing组件大都将外观显示和内部数据分离,JProgressBar组件有一个内置的用于保存其状态数据的Model对象,这个对象由BoundedRangeModel对象表示,程序调用JProgressBar对象的方法完成进度百分比的设置,监听进度条的数据变化,其实都是通过它内置的BoundedRangeModel对象完成的。对上方的代码进行改进,通过BoundedRangeModel完成数据的设置,获取,和监听

package com.swing.xm.jprogress_bar;

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

/**
 * JProgressBarModelTest:
 *
 * @author IlffQ
 * 2022/8/4
 */
public class JProgressBarModelTest {
    JFrame jFrame = new JFrame("测试进度条");
    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");
    JProgressBar bar = new JProgressBar(SwingConstants.HORIZONTAL, 0, 100);
    public void init() {
        //获取Model对象,即获取处理进度条内置的数据模型的对象
        BoundedRangeModel model = bar.getModel();
        // 处理监听
        indeterminate.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean selected = indeterminate.isSelected();
                bar.setIndeterminate(selected);
                bar.setStringPainted(!selected);
            }
        });
        noBorder.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                boolean selected = noBorder.isSelected();
                bar.setBorderPainted(!selected);
            }
        });

        //设置属性(初始)
        //1.绘制百分百比
        bar.setStringPainted(true);
        //2.绘制边框
        bar.setBorderPainted(true);

        //组装视图
        Box box = Box.createVerticalBox();
        box.add(indeterminate);
        box.add(noBorder);
        Box box1 = Box.createHorizontalBox();
        box1.add(box);
        box1.add(bar);
        jFrame.add(box1);
        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //模拟进度
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {

                model.setValue(i + 1);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }).start();

    }
    public static void main(String[] args) {
        new JProgressBarTest().init();
        System.out.println("你好");
    }
}

ProgressMonitor 创建进度对话框

ProgressMonitor的用法和JProgressBar的用法相似,只是ProgressMonitor可以直接创建一个进度对话框,他提供了下面的构造器完成进度对话框的创建

public ProgressMonitor(Component parentComponent,Object message,String not,int min,int max):
       parentComponent:对话框的父组件
       message:对话框的描述信息
       note:进度的提示信息
       min:进度条的最小值
       max:进度条的最大值

使用ProgressMonitor创建的对话框里包含的进度条是固定的,通过代码不能改变进度条是否包含边框,也不能设置进度不确定,不能改变进度条的方向,只能是水平的

案例演示:

package com.swing.xm.jprogress_bar;

import javax.swing.*;

/**
 * JProgressMonitorTest:
 *
 * @author IlffQ
 * 2022/8/4
 */
public class JProgressMonitorTest {

    public void init() {
        ProgressMonitor progressMonitor = new ProgressMonitor(null, "等待任务完成", "已经完成", 0, 100);
        new Thread(() -> {
            for (int i = 1; i <= 100; i++) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                progressMonitor.setProgress(i);
                if (progressMonitor.isCanceled()) {
                    progressMonitor.close();
                    System.exit(0);
                }
            }
        }).start();
    }

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

swing JList,JComboBox实现 列表框

JList和JComboBox都是极为相似的,他们都有一个列表框,JComboBox的列表框需要下拉的方式展示出来;JList和JComboBox都可以通过调用setRenderer()方法来改变列表项的表现形式。维护这两个组件的Model都是相似的,JList使用ListModel,JComboBox使用ComboBoxModel,而ComboBox是ListModel的子类。

JList,JComboBox实现简单列表框

1.创建JList或JComboBox对象

JList(final E[] listData):创建JList对象,把listData数组中的每一项内容转换成一个列表项展示
JList(final Vector<? extends E> listData):创建JList对象,将listData 数组中的每一项内容转化成一个列表项展示

JComboBox(E[] items):创建JComboBox对象,把listData数组中的每一项内容转换成一个列表项展示
JComboBox(Vector<E> items):创建JComboBox对象,把listData数组中的每一项内容转换成一个列表项展示

2.设置JList或JComboBox的外观行为

~~~~~~~jList~~~~~~~
addSelectionInterval(int anchor,int lead):在已经选中列表项的基础上,增加选中anchor到lead索引范围内的所有列表项
setFixedCellHeight(int height)/setFixedCellWidth(int width):设置列表的高度和宽度
setLayoutOrientation(int layoutOrentation):设置列表框的布局方向
setSelectedIndex(int index):设置默认选中项
setSelectedIndeces(int[] indices):设置默认选中的多个列表框
//TODO 浪费时间

~~~~~~JcomboBox~~~~~


3.设置监听器,监听列表项的变化,JList通过addListSelectionListener完成,JComboBox通过addItemListener完成

package com.swing.xm.JListAndJComboBox;

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.util.List;

/**
 * Demo1:
 * 简单链表的实现
 *
 * @author IlffQ
 * 2022/8/5
 */
public class Demo1 {
    JFrame jFrame = new JFrame("列表框测试");
    String[] books = {"java自学宝典", "Javaee企业应用实践",
            "Android基础教程", "Junit单元测试", "SpringBoot企业开发"};

    JPanel layoutPanel = new JPanel();
    ButtonGroup layoutButton = new ButtonGroup();

    JPanel selectModePanel = new JPanel();
    ButtonGroup selectModeGroup = new ButtonGroup();

    JTextArea myBook = new JTextArea(4, 40);

    JList<String> bookList;
    JComboBox<String> bookSelector;

    public void init() {


        bookList = new JList<>(books);
        addBtnLayoutPanel("纵向滚动", JList.VERTICAL);
        addBtnLayoutPanel("纵向换行", JList.VERTICAL_WRAP);
        addBtnLayoutPanel("横向换行", JList.HORIZONTAL_WRAP);
        addBtnModelPanel("无限制", ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        addBtnModelPanel("单选", ListSelectionModel.SINGLE_SELECTION);
        addBtnModelPanel("单范围", ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        //可视范围
        bookList.setVisibleRowCount(3);
        bookList.setSelectionInterval(2, 4);
        Box box = Box.createVerticalBox();
        box.add(new JScrollPane(bookList));
        box.add(layoutPanel);
        box.add(selectModePanel);


        bookList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                List<String> selectedValuesList =
                        bookList.getSelectedValuesList();
                myBook.setText("");
                for (String s : selectedValuesList) {
                    myBook.append(s + "\n");
                }
            }
        });

        bookSelector = new JComboBox<>(books);
        Box box1 = Box.createHorizontalBox();
        box1.add(box);
        JPanel bookSelectorPanel = new JPanel();
        bookSelectorPanel.add(bookSelector);
        box1.add(bookSelectorPanel);

        bookSelector.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
//                int index = bookSelector.getSelectedIndex();
//                myBook.setText(books[index]);
                Object selectedItem = bookSelector.getSelectedItem();
                myBook.setText(selectedItem.toString());
            }
        });
        JPanel jPanel = new JPanel();
        jPanel.add(new JLabel("选中的图书:"), BorderLayout.NORTH);
        jPanel.add(myBook, BorderLayout.SOUTH);
        Box verticalBox = Box.createVerticalBox();
        verticalBox.add(box1);
        verticalBox.add(jPanel);

        jFrame.add(verticalBox);
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


    }

    public void addBtnLayoutPanel(String name, int layoutType) {
        layoutPanel.setBorder(new TitledBorder(new EtchedBorder(), "确定选项布局"));
        JRadioButton button = new JRadioButton(name);
        layoutPanel.add(button);
        if (layoutButton.getButtonCount() == 0)
            button.setSelected(true);
        layoutButton.add(button);
        button.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                bookList.setLayoutOrientation(layoutType);
            }
        });
    }

    public void addBtnModelPanel(String name, int selectModel) {
        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 AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                bookList.setSelectionMode(selectModel);
            }
        });
    }

    public static void main(String[] args) {
        new Demo1().init();
    }
}
JList,JComboBox不通过组件干涉,完成需求MVC架构

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

整个JListModel不管JList里面的所有列表项的存储形式,他甚至不强制存储所有的列表项,只要ListModel的实现类提供了getSize()和getElementAt() 两个方法,JList就可以根据该JListModel对象来生成列表框。ComboBoxModel 继承了ListModel,他添加了“选择项”的概念,选择项代表JComboBox现实的区域内可见的列表框。

自定义NumberListModel和NumberComboBoxModel实现类,允许使用数值范围来创建JLIst和JComboBox

package com.swing.xm.JListAndJComboBox;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;

/**
 * Demo2:
 * JList,JComboBox不通过组件干涉,完成需求MVC架构
 *
 * @author IlffQ
 * 2022/8/11
 */
public class Demo2 {
    JFrame jFrame=new JFrame("测试手写Model");
    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 jLabel=new JLabel("你选择的值是:");
    JTextField jTextField=new JFormattedTextField(15);

    public void init()
    {
        jList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                List<BigDecimal> selectedValuesList = jList.getSelectedValuesList();
                //清空
                jTextField.setText("");
                for (BigDecimal decimal : selectedValuesList) {
                     jTextField.setText(jTextField.getText()+decimal.toString()+",");
                }
            }
        });

        jList.setVisibleRowCount(4);
        jList.setSelectionInterval(2,4);
        jList.setFixedCellWidth(90);
        jList.setFixedCellHeight(30);

        jComboBox.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        jComboBox.setMaximumRowCount(4);
        jComboBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                Object selectedItem = jComboBox.getSelectedItem();
                assert selectedItem != null;
                jTextField.setText(selectedItem.toString());
            }
        });


        Box horizontalBox = Box.createHorizontalBox();
        JPanel listPanel = new JPanel();
        JPanel comboBoxPanel=new JPanel();
        listPanel.add(new JScrollPane(jList));
        comboBoxPanel.add(jComboBox);
        horizontalBox.add(listPanel);
        horizontalBox.add(comboBoxPanel);
        jFrame.add(horizontalBox);

        JPanel jPanel = new JPanel();
        jPanel.add(jLabel);
        jPanel.add(jTextField);
        jFrame.add(jPanel, BorderLayout.SOUTH);

        jFrame.pack();
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public static void main(String[] args) {
            new Demo2().init();
    }

}

//自定义Model对象来完成指定的功能,不用组件自带的Model,通常情况下会在新建的Model包中编写代码
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() {
        BigDecimal i = end.subtract(start).divide(step, 2, RoundingMode.HALF_DOWN);
        return i.intValue() + 1;
    }

    @Override
    public BigDecimal getElementAt(int index) {
        return new BigDecimal(index).multiply(step).add(start).setScale(2, RoundingMode.HALF_DOWN);
    }
}

class NumberComboBoxListModel extends NumberListModel implements ComboBoxModel<BigDecimal> {
    public NumberComboBoxListModel(BigDecimal start, BigDecimal end, BigDecimal step) {
        super(start, end, step);
    }

    //当前选中的条目
    private int selectedId;


    @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() {
        return new BigDecimal(selectedId).multiply(super.step).add(super.start)
                .setScale(1, RoundingMode.HALF_DOWN);

    }
}
强制存储列表项的DefaultListModel和DefaultComboBoxMode完成需求MVC架构
package com.swing.xm.JListAndJComboBox;

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

/**
 * Demo3:
 * 强制存储列表项的DefaultListModel和DefaultComboBoxMode完成需求MVC架构
 *
 * @author IlffQ
 * 2022/8/17
 */
public class Demo3 {
    JFrame jFrame = new JFrame("测试DefaultListModel");
    JTextField bookName = new JTextField(20);
    JButton removeBtn = new JButton("删除选中图书");
    JButton addBtn = new JButton("添加指定图书");
    JList bookList;
    DefaultListModel<String> model = new DefaultListModel<>();

    public void init() {
        model.addElement("算法大师");
        model.addElement("恋爱大师");
        model.addElement("篮球大师");
        model.addElement("排球大师");
        model.addElement("法律大师");
        bookList = new JList(model);
        bookList.setVisibleRowCount(4);
        bookList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        jFrame.add(new JScrollPane(bookList));
        JPanel jPanel = new JPanel();
        jPanel.add(bookName);
        jPanel.add(addBtn);
        jPanel.add(removeBtn);
        jFrame.add(jPanel, BorderLayout.SOUTH);
        addBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!bookName.getText().trim().equals(""))
                    model.addElement(bookName.getText());
            }
        });
        removeBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Object selectedValue = bookList.getSelectedValue();
                model.removeElement(selectedValue);
            }
        });
        jFrame.setVisible(true);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new Demo3().init();
    }
}
ListCellRenderer绘制列表组件外观

JList和JComboBox还支持图标列表项,如果在创建JList或JComboBox时传入图标数组,则创建的JList和JComboBox的列表项就是图标。如果希望列表项是更加复杂的组件,如既有图标,又有字符串的列表项,就要使用ListCellRenderer接口的实现类对象,自定义每个条目的渲染过程

public interface ListCellRenderer<E>{
    Component getListCellRendererComponent(JList<?extends E> list,
                                          E value,
                                          int index,
                                          boolean isSelected,
                                          boolean cellHasFocus);
}

通过JList的setCellRenderer(ListCellRenderer<?super E>)方法,把自定义的 ListCellRenderer对象传递给JList,就可以按照自定义的规则绘制列表项组件了。

package com.swing.xm.JListAndJComboBox;

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

/**
 * Demo4:
 * ListCellRenderer绘制列表组件外观
 *
 * @author IlffQ
 * 2022/8/17
 */
public class Demo4 {
    JFrame jFrame = new JFrame("宫本武藏列表");
    private String[] friends = {
            "宫本武藏",
            "二天一流",
            "空明斩",
            "狩魔"
    };
    JList list = new JList(friends);

    public void init() {
        list.setCellRenderer(new MyListCellRenderer());
        jFrame.add(list);

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

    private class MyListCellRenderer 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("B\\img\\component\\ok1.png");
            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.getIconWidth();
            int imageHeight = icon.getIconHeight();
            g.setColor(backGround);
            g.fillRect(0, 0, getWidth(), getHeight());

            g.drawImage(icon.getImage(), this.getWidth() / 2 - imageWidth / 2, this.getHeight() / 2 - imageHeight / 2, null);
            g.setColor(forceGround);
            g.setFont(new Font("StSong", Font.BOLD, 18));
            g.drawString(this.name, this.getWidth() / 2 - this.name.length() * 20 / 2, 10 + imageHeight + 20);
        }
    }

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

JTree

JTree基本使用

按照结点是否包含子结点,可以把结点分为两类

  • 普通结点,包含叶子结点的节点。
  • 叶子结点,没有子结点的结点。

按照节点是否具有唯一的结点,可以把结点分成两类

  • 根结点,没有父结点,一棵树只有一个根结点
  • 普通结点,具有唯一父结点的结点。

JTree常用的构造方法:

JTree(TreeModel newModel):使用指定的数据模型创建JTree对象,默认显示根节点
JTree(TreeNode root):使用root作为根节点创建JTree对象,默认显示根节点
JTree(TreeNode root,boolean asksAllowsChildren):使用root作为根节点创建JTree对象,默认显示根节点,askAllowsChildren参数控制怎么样的结点才算叶子结点,只有当程序使用setAllowsCuhildren(false)显示设置某个结点不允许添加子结点(以后也不会拥有子节点),该节点才会被JTree当成叶子节点;如果该参数为false
 ,则只要某个结点当时没有子结点(不管以后是否拥有字结点(),该节点都会被JTree当成叶子节点。

TreeNode的继承体系:

TreeNode 接口是 MutableTreeNode接口的父接口,MutableTreeNode接口是DefaultMutableTreeNode接口的父接口

JTree的其他外观设置方法:

tree.putClientProperty("JTree.lineStyle","None"):设置节点之间没有连线
tree.putClentProperty("JTree.lineStyle","Horizontal"):设置节点之间只有水平分割线
 TreeNode[]getPath():返回从根节点到达此节点的所有节点组成的路径
 
编辑JTree结点

JTree生成的树默认是不可编辑的,不可以添加或删除结点,也不可以改变结点数据:如果想让某个JTree对象编程可编辑的状态,则可以调用JTree的setEditable(true),方法,把这个树变成可编辑的树。

1.获取结点:

1.
    通过JTree对象的某些方法,例如TreePath getSelectionPath()等,得到一个TreePath对象,包含从根节点到当前节点路径上的所有结点。
    调用TreePath对象的Object getLastPathComponent()方法,得到当前选中的结点
2.
    通过JTree对象的的Object getLastSelectedPahtComponent()方法获取当前被选中的结点

2.调用DefaultTreeModel数据模型有关增删改的一系列方法完成编辑,方法执行完成后,重绘JTree

package com.swing.xm.tree;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;

/**
 * Demo1:
 * tree的基本使用,编辑树节点
 *
 * @author IlffQ
 * 2022/8/18
 */
public class Demo1 {
    JFrame jFrame = new JFrame("简单树");
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("中");
    DefaultMutableTreeNode gd = new DefaultMutableTreeNode("广");
    DefaultMutableTreeNode gx = new DefaultMutableTreeNode("西");
    DefaultMutableTreeNode fs = new DefaultMutableTreeNode("山");
    DefaultMutableTreeNode st = new DefaultMutableTreeNode("头");
    DefaultMutableTreeNode gl = new DefaultMutableTreeNode("林");
    DefaultMutableTreeNode nn = new DefaultMutableTreeNode("宁");
    JTree jTree = new JTree(root);
    JButton addSiblingBtn = new JButton("添加兄弟节点");
    JButton addChildBtn = new JButton("添加子节点");
    JButton deleteBtn = new JButton("删除结点");
    JButton editBtn = new JButton("编辑结点");

    public void init() {
        root.add(gd);
        root.add(gx);
        gd.add(fs);
        gd.add(st);
        gx.add(gl);
        gx.add(nn);
//        jTree.putClientProperty("JTree.lineStyle","None");
//        jTree.putClientProperty("JTree.lineStyle","Horizontal");
//        TreeNode parent = gd.getParent();
//        System.out.println(parent);
//        TreeNode[] path = nn.getPath();
//        System.out.println(Arrays.toString(path));
//        System.out.println(nn.isLeaf());
        //完成节点编辑代码
        jTree.setEditable(true);
        DefaultTreeModel model = (DefaultTreeModel) jTree.getModel();
        addSiblingBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Object lastSelectedPathComponent = jTree.getLastSelectedPathComponent();
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) lastSelectedPathComponent;

                if (selectedNode == null)
                    return;
                TreeNode parent = selectedNode.getParent();
                DefaultMutableTreeNode node = new DefaultMutableTreeNode("新节点");
                DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) parent;
                if (parentNode == null)
                    return;
                int index = parentNode.getIndex(selectedNode);
                model.insertNodeInto(node, parentNode, index);
                //显示结点
                TreeNode[] pathToRoot = model.getPathToRoot(node);
                TreePath treePath = new TreePath(pathToRoot);
                jTree.scrollPathToVisible(treePath);
                //重绘结点
                jTree.updateUI();
            }
        });
        addChildBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {

                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
                if (selectedNode == null)
                    return;
                DefaultMutableTreeNode node = new DefaultMutableTreeNode("新节点");
                selectedNode.add(node);
                TreeNode[] pathToRoot = model.getPathToRoot(node);
                TreePath treePath = new TreePath(pathToRoot);
                jTree.scrollPathToVisible(treePath);
                jTree.updateUI();
            }
        });
        deleteBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) jTree.getLastSelectedPathComponent();
                if (selectedNode != null && selectedNode.getParent() != null) {
                    model.removeNodeFromParent(selectedNode);
                }
            }
        });
        editBtn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                TreePath selectedPath = jTree.getSelectionPath();
                if (selectedPath != null)
                    jTree.startEditingAtPath(selectedPath);
            }
        });
        jFrame.add(new JScrollPane(jTree));
        JPanel jPanel = new JPanel();
        jPanel.add(addSiblingBtn);
        jPanel.add(addChildBtn);
        jPanel.add(deleteBtn);
        jPanel.add(editBtn);
        jFrame.add(jPanel, BorderLayout.SOUTH);

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

    public static void main(String[] args) {
        new com.swing.xm.tree.Demo1().init();
    }
}
JTree结点添加监听器

为JTree 添加监听器:

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

修改JTree的选择模式

JTree专门提供了一个TreeSelectionModel对象类保存该JTree选中状态的信息。也就是说,JTree组件的背后隐藏了两个model对象。其中TreeModel用于保存该JTree的所有节点数据,而TreeSelectionModel用于保存该JTree的所有选中状态的信息。

程序可以改变JTree的选择模式,但是必须先获取JTree对应的JTreeSelectionModel对象,再调用该对象的setSelectionMode(int mode) 方法来设置该JTree的选择模式,其中model可以由如下3中取值:

  1. TreeSelectionModel.CONTIGUOUS_TREESELECTION:可以连续选中多个TreePath;
  2. TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION:该选项对于选择没有任何限制。
  3. TreeSelectionModel.SINGLE_TREE_SELECTION:每次只能选择一个TreePath。
package com.swing.xm.tree;

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

/**
 * Demo2:
 * JTree事件监听
 *
 * @author IlffQ
 * 2022/8/18
 */
public class Demo2 {
    JFrame jFrame = new JFrame("JTree事件监听");
    DefaultMutableTreeNode root = new DefaultMutableTreeNode("中");
    DefaultMutableTreeNode gd = new DefaultMutableTreeNode("东");
    DefaultMutableTreeNode gx = new DefaultMutableTreeNode("西");
    DefaultMutableTreeNode fs = new DefaultMutableTreeNode("山");
    DefaultMutableTreeNode st = new DefaultMutableTreeNode("头");
    DefaultMutableTreeNode gl = new DefaultMutableTreeNode("林");
    DefaultMutableTreeNode nn = new DefaultMutableTreeNode("宁");
    JTextArea jTextArea = new JTextArea(5, 20);

    public void init() {
        root.add(gd);
        root.add(gx);
        gd.add(fs);
        gd.add(st);
        gx.add(gl);
        gx.add(nn);
        JTree jTree = new JTree(root);

        //设置选择模式
        TreeSelectionModel sm = jTree.getSelectionModel();
        sm.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        //设置监听器
        jTree.addTreeSelectionListener(e -> {
            TreePath path = e.getNewLeadSelectionPath();
            jTextArea.append(path.toString() + "\n");
        });
        Box box = Box.createHorizontalBox();
        box.add(new JScrollPane(jTree));
        box.add(new JScrollPane(jTextArea));
        jFrame.add(box);
        jFrame.pack();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setVisible(true);
    }

    public static void main(String[] args) {
        new Demo2().init();
    }
}
使用DefalutTreeCellTenderer改变节点的外观

JTree默认的外观是比较单一的,它提供了如下几种改变结点外观的方式

  1. 使用DefaultTreeCellRenderer直接改变结点的外观,这种方式可以改变整个树所有结点的字体,颜色,和图标
  2. 为JTree指定DefaultTreeCellRenderer的扩展类对象作为JTree的结点绘制器,该绘制器负责为不同结点使用不同的字体颜色和图标,通常使用这种方法类改变节点的外观。
  3. 为JTree指定一个实现TreeCellRenderer接口的结点绘制器,该绘制器可以为不同的结点自由的绘制任意内容,这个是最复杂,也是最灵活的结点绘制器。

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

setBackgroundNonSelectionColor(Color newColor):设置用于非选中结点的背景颜色。
setBackgroundSelectionColor(Color new Color):设置结果i请按在选中状态下的背景颜色。
setBorderSelectionColor(Color new Color):设置选中状态下结点的边框颜色
setCloseedIcon(Icon new Icon):设置处于折叠状态下非叶子结点的图标
setFont(Font font):设置结点文本的字体
setLeafIcon(Icon new Icon):设置叶子节点的图标
setOpenIcon (Icon newIcon):设置出入展开状态下非叶子结点的图标
setTextNonSelectionColor(Color new Color):设置绘制非选中状态下结点文本的颜色。
setTextSelectionColor(Color new Color):设置绘制选中状态下节点文本的颜色
扩展DefaultTreeCellREnderer改变结点外观

DefaultTreeCellRenderer实现类实现了TreeCellRenderer接口,该接口类只有一个用于绘制结点内容的方法:getTreeCellRendererComponent(),该方法负责绘制JTree结点。 DefaultTreeCellRenderer类继承了JLabel,实现getTreeCellTendererComponent()方法时返回this,即返回一个特殊的JLable对象。如果需要根据结点内容来改变节点的外观,则可以再次扩展DefaultTreeCellRenderer类,并再次重写它提供的getTreeCellRendererComponent()方法。

实现TreeCellRenderer接口改变结点的外观

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

继承接口的优势:默认的继承DefaultTreeCellRenderer实现类继承JLabel有非常大的局限性,继承TreeCellRenderer接口的实现类可以自定义继承任意的Component 组件,非常人性化。

实践:自定义类,继承JPanel类,实现TreeCellRenderer接口,完成下图效果:

package com.swing.xm.tree;

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

/**
 * Demo4:
 * TreeCellRenderer接口的实现类
 *
 * @author IlffQ
 * 2022/8/18
 */
public class Demo4 {
    JFrame jFrame = new JFrame("继承TreeCellRenderer接口,实现外观整改");
    DefaultMutableTreeNode skills = new DefaultMutableTreeNode("技能");
    DefaultMutableTreeNode skill1 = new DefaultMutableTreeNode("空明斩");
    DefaultMutableTreeNode skill2 = new DefaultMutableTreeNode("二天一流");
    DefaultMutableTreeNode skill3 = new DefaultMutableTreeNode("狩魔");
    JTree tree;

    public void init() {
        skills.add(skill1);
        skills.add(skill2);
        skills.add(skill3);
        tree = new JTree(skills);

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

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

    private class MyCellRenderer 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("B\\img\\component\\ok1.png");
            this.name = value.toString();
            this.background = selected ? Color.BLACK : Color.RED;
            this.foreground = selected ? Color.WHITE : Color.BLUE;

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

JTable TableModel实现表格

表格是一个由多行,多列形成的二维显示区。swing的JTable以及相关的类提供了这种表格支持,通过使用JTable和相关类,可以使用简单的代码创建出来二维表格显示数据,也可以开发出功能丰富的表格,还可以为表格定制各种显示外观,编辑特性。

创建并调整简单的表格

使用JTable 创建一个简单的表格

  1. 创建一个一维数组,存储表格中每一列的标题
  2. 创建一个二维数组,纯种表格中每行数据,其中二维数组内部的每一个一维数组,代表表格中的一行数据。
  3. 根据第一步和第二步创建的一维数组和二维数组,创建JTable对象
  4. 把JTable 添加到其他容器中。

JTable调整列宽:

JTable使用TableColumn来表示表格中的每一列,JTable中表格列的所有属性,如最佳宽度,是否可调整宽度,最小和最大宽度等都保存在该TableColumn 中。

  1. setMaxWidth(int maxWidth):设置该列的最大宽度。如果指定的maxWidth小于该列的最小宽度,则maxWidth被设置成最小宽度。
  2. setMinWidth(int minWidth):设置该列的最小宽度。
  3. setPreferredWidth(int perferredWidth):设置该列的最佳宽度

JTable调整表格选择模式

和JList类似,JTable使用了一个ListSelectionModel表示该表格的选择状态,程序可以通过ListSelectionModel.seSelectionModel(int model)控制JTable的选择模式。JTable的选择模式有以下三种:

  1. ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:没有任何限制
  2. ListSelectionModel.SINGLE_SELECTION:只能选择单行
  3. ListSelectionModel.SINGLE_INTERVAL_SELECTION:选择单个连续区域
自定义类MyTableModel类 继承AbstractTableModel抽象类

自定义方法继承AbstractTableModel 抽象类,重写下面几个方法

int getColumnCount():返回表格列的数量
int getRowCount():返回表格行的数量
Object getValueAt(int rowIndex,int columnIndex):返回rowIndex行,column列的单元格的值
String getColumnName(int columnIndex):返回columnIndex列的列名称
boolean isCellEditable(int rowIndex,int columnIndex):设置rowIndex行,columnIndex列单元格是否可以编辑。
package com.swing.xm.jtable;

import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Vector;

/**
 * Demo2:
 * MyTableModel extends AbstractTableModel
 *
 * @author IlffQ
 * 2022/8/19
 */
public class Demo2 {
    JFrame jFrame = new JFrame("表格");
    Object[] titles = {"姓名", "年龄", "性别"};
    Object[][] data = {
            new Object[]{"小鸟一号", 1, "男"},
            {"小鸟二号", 2, "男"},
            {"小鸟三号", 3, "女"},
            {"小鸟四号", 4, "男"},
            {"小鸟五号", 5, "女"}
    };
    private Vector titleV = new Vector();//存储标题
    private Vector<Vector> dataV = new Vector<>();//存储数据

    public void init() {
        for (int i = 0; i < titles.length; i++) {
            titleV.add(titles[i]);
        }

        for (int i = 0; i < data.length; i++) {
            Vector t = new Vector();
            for (int i1 = 0; i1 < data[i].length; i1++) {
                t.add(data[i][i1]);
            }
            dataV.add(t);
        }
        //JTable table = new JTable(dataV,titleV);
        JTable table = new JTable(new MyTableModel());
        Button button = new Button();
        button.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int column = table.getSelectedColumn();
                int row = table.getSelectedRow();
                System.out.println("选中行数据:");
                System.out.println(dataV.get(row));
            }
        });
        jFrame.add(button, BorderLayout.SOUTH);

        jFrame.add(new JScrollPane(table));
        jFrame.setVisible(true);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.pack();
    }

    private class MyTableModel extends AbstractTableModel {

        @Override
        public int getRowCount() {
            return dataV.size();
        }

        @Override
        public int getColumnCount() {
            return titleV.size();
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return dataV.get(rowIndex).get(columnIndex);
        }

        @Override
        public String getColumnName(int column) {
            return (String) titleV.get(column);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }
    }

    public static void main(String[] args) {
        new Demo2().init();
    }
}
默认DefaultTableModel 类继承TableModel接口

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

addColumn(Oject columnName)/ addColumn(Object columnName,Object[] columnData):添加一列
addRow(Object [] rowData):添加一行
insertRow(int row,Object[] rowData):指定位置插入一行
removeRow(int Row):删除一行     
moveRow(int start,int end,int to):移动指定范围内的数据行
package com.swing.xm.jtable;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Vector;

/**
 * Demo3:
 * 实现继承TableModel接口的默认的DefaultTableModel类
 *
 * @author IlffQ
 * 2022/8/19
 */
@SuppressWarnings("all")
public class Demo3 {
    JFrame jFrame = new JFrame("表格");
    Object[] titles = {"姓名", "年龄", "性别"};
    Object[][] data = {
            new Object[]{"小鸟一号", 1, "男"},
            {"小鸟二号", 2, "男"},
            {"小鸟三号", 3, "女"},
            {"小鸟四号", 4, "男"},
            {"小鸟五号", 5, "女"}
    };
    private Vector titleV = new Vector();//存储标题
    private Vector<Vector> dataV = new Vector<>();//存储数据

    public void init() {
        for (int i = 0; i < titles.length; i++) {
            titleV.add(titles[i]);
        }

        for (int i = 0; i < data.length; i++) {
            Vector t = new Vector();
            for (int i1 = 0; i1 < data[i].length; i1++) {
                t.add(data[i][i1]);
            }
            dataV.add(t);
        }

        JButton addRow = new JButton("添加一行");
        JButton addColumn = new JButton("添加一列");
        JButton removeRow = new JButton("删除一行");
        DefaultTableModel model = new DefaultTableModel(dataV, titleV);
        JTable table = new JTable(model);
        addRow.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.addRow(new Object[]{"小鸟无敌号", 18, "男"});
            }
        });
        addColumn.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.addColumn("地址");
            }
        });
        removeRow.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.removeRow(table.getSelectedRow());
            }
        });
        jFrame.add(new JScrollPane(table));
        JPanel jPanel = new JPanel();
        jPanel.add(addRow);
        jPanel.add(addColumn);
        jPanel.add(removeRow);
        jFrame.add(jPanel, BorderLayout.SOUTH);

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

    public static void main(String[] args) {
        new Demo3().init();
    }
}
  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ILFFQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值