【Java基础】swing-图形界面学习(下)

就是个人学习的笔记,按照下面的Demo一个一个复制粘贴跑起来大概就会使用Swing了,建议先从上半部分开始看

上半部分

【Java基础】swing-图形界面学习(上)

八.菜单

GUI的菜单分为 菜单栏JMenuBar菜单JMenu菜单项JMenuItem

8.1.菜单栏和菜单

在这里插入图片描述

    @Test
    public void MenuAndJMenuBar() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);

        // 菜单栏
        JMenuBar mb = new JMenuBar();

        // 菜单
        JMenu mHero = new JMenu("英雄");
        JMenu mItem = new JMenu("道具");
        JMenu mWord = new JMenu("符文");
        JMenu mSummon = new JMenu("召唤师");
        JMenu mTalent = new JMenu("天赋树");

        // 把菜单加入到菜单栏
        mb.add(mHero);
        mb.add(mItem);
        mb.add(mWord);
        mb.add(mSummon);
        mb.add(mTalent);

        // 把菜单栏加入到frame,这里用的是set而非add
        frame.setJMenuBar(mb);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

8.2.菜单项

在这里插入图片描述

@Test
    public void MenuItem() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 400);
        frame.setLocation(200, 200);

        JMenuBar mb = new JMenuBar();

        JMenu mHero = new JMenu("英雄");
        JMenu mItem = new JMenu("道具");
        JMenu mWord = new JMenu("符文");
        JMenu mSummon = new JMenu("召唤师");
        JMenu mTalent = new JMenu("天赋树");

        // 菜单项
        mHero.add(new JMenuItem("近战-Warriar"));
        mHero.add(new JMenuItem("远程-Range"));
        mHero.add(new JMenuItem("物理-physical"));
        mHero.add(new JMenuItem("坦克-Tank"));
        mHero.add(new JMenuItem("法系-Mage"));
        mHero.add(new JMenuItem("辅助-Support"));
        mHero.add(new JMenuItem("打野-Jungle"));
        mHero.add(new JMenuItem("突进-Charge"));
        mHero.add(new JMenuItem("男性-Boy"));
        mHero.add(new JMenuItem("女性-Girl"));
        // 分隔符
        mHero.addSeparator();
        mHero.add(new JMenuItem("所有-All"));

        mb.add(mHero);
        mb.add(mItem);
        mb.add(mWord);
        mb.add(mSummon);
        mb.add(mTalent);

        frame.setJMenuBar(mb);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

8.3.练习-完成一个完整的记事本界面

在这里插入图片描述

    @Test
    public void NotePad() throws InterruptedException {
    
    
        //创建容器
        JFrame frame = new JFrame("记事本");
        //
        frame.setSize(400, 400);
        frame.setLocation(200, 200);

        //创建菜单栏
        JMenuBar menuBar = new JMenuBar();

        //创建菜单
        JMenu document = new JMenu("文件(A)");
        JMenu editor = new JMenu("编辑(E)");
        JMenu layout = new JMenu("格式(O)");
        JMenu viewing = new JMenu("查看(V)");
        JMenu help = new JMenu("帮助(H)");

        // 文件菜单添加菜单项
        document.add(new JMenuItem("新建(N)                         Ctrl+N"));
        document.add(new JMenuItem("打开(O)                         Ctrl+O"));
        document.add(new JMenuItem("保存(S)                         Ctrl+S"));
        document.add(new JMenuItem("另存为(N)"));
        document.addSeparator();//分割线
        document.add(new JMenuItem("页面设置(U)"));
        document.add(new JMenuItem("打印(P)                         Ctrl+P"));
        document.addSeparator();//分割线
        document.add(new JMenuItem("退出(X)"));

        // 编辑项菜单添加菜单项
        editor.add(new JMenuItem("撤销(U)                         Ctrl+Z"));
        editor.addSeparator();//分割线
        editor.add(new JMenuItem("剪切(T)                         Ctrl+X"));
        editor.add(new JMenuItem("复制(C)                         Ctrl+C"));
        editor.add(new JMenuItem("粘贴(P)                         Ctrl+V"));
        editor.add(new JMenuItem("删除(L)                             Del"));
        editor.addSeparator();//分割线
        editor.add(new JMenuItem("查找(frame)                         Ctrl+frame"));
        editor.add(new JMenuItem("查找下一个(N)                    F3"));
        editor.add(new JMenuItem("替换(R)                         Ctrl+H"));
        editor.add(new JMenuItem("转到(G)                         Ctrl+G"));
        editor.addSeparator();//分割线
        editor.add(new JMenuItem("全选(A)                         Ctrl+A"));
        editor.add(new JMenuItem("时间/日期(D)                       F5"));
        //格式菜单添加菜单项
        layout.add(new JMenuItem("自动换行(W)            "));
        layout.add(new JMenuItem("字体(frame)"));
        //查看菜单添加菜单项
        viewing.add(new JMenuItem("状态栏(S)"));
        //帮助菜单添加菜单项
        help.add(new JMenuItem("查看帮助(H)"));
        help.add(new JMenuItem("关于记事本(A)"));

        //菜单加入菜单栏
        menuBar.add(document);
        menuBar.add(editor);
        menuBar.add(layout);
        menuBar.add(viewing);
        menuBar.add(help);

        //多行文本框
        JTextArea textArea = new JTextArea();
        textArea.setLineWrap(true);
        //加入滚动条面版
        JScrollPane scrollPane = new JScrollPane(textArea);

        //滚动面板加入容器
        frame.setContentPane(scrollPane);
        //容器加入菜单栏
        frame.setJMenuBar(menuBar);
        //设置默认关闭操作
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置容器可显示
        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

九.工具栏

工具栏用于存放常用的按钮

btn.setToolTipText() 可以设置按钮提示信息
toolBar.setFloatable(false);禁止工具栏拖动:true可以拖动 false不可以拖动
在这里插入图片描述

    @Test
    public void JToolBarTest() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        // 添加菜单
        addMenu(frame);

        // 工具栏
        JToolBar toolBar = new JToolBar();
        // 为工具栏增加按钮
        JButton b1 = new JButton(new ImageIcon("e:/data/1.jpg"));
        //设置按钮提示文字
        b1.setToolTipText("坑爹英雄");
        JButton b2 = new JButton(new ImageIcon("e:/data/2.jpg"));
        JButton b3 = new JButton(new ImageIcon("e:/data/3.jpg"));
        JButton b4 = new JButton(new ImageIcon("e:/data/4.jpg"));
        JButton b5 = new JButton(new ImageIcon("e:/data/5.jpg"));
        JButton b6 = new JButton(new ImageIcon("e:/data/6.jpg"));
        toolBar.add(b1);
        toolBar.add(b2);
        toolBar.add(b3);
        toolBar.add(b4);
        toolBar.add(b5);
        toolBar.add(b6);

        // 禁止工具栏拖动
        toolBar.setFloatable(false);
        // 把工具栏放在north的位置
        frame.setLayout(new BorderLayout());
        frame.add(toolBar, BorderLayout.NORTH);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

十.日期控件

10.1.DatePicker

<dependency>
  <groupId>org.jdatepicker</groupId>
  <artifactId>jdatepicker</artifactId>
  <version>1.3.4</version>
</dependency>

本例使用datepicker.jar 包,有一个缺点,不能设置时间,只能在创建控件的时候传入指定日期。
需要设置日期,请使用JXDatePicker
在这里插入图片描述

import com.eltima.components.ui.DatePicker;
public class TestGUI {
    
    
	public static void main(String[] args) {
    
    
  
        JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(200, 200);
        f.setLayout(null);
  
        final DatePicker datepick;
        datepick = getDatePicker();
  
        f.add(datepick);
  
        JButton b = new JButton("获取时间");
        b.setBounds(137, 183, 100, 30);
        f.add(b);
  
        b.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                JOptionPane.showMessageDialog(f, "获取控件中的日期:" + datepick.getValue());
                System.out.println(datepick.getValue());
            }
        });
  
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
        f.setVisible(true);
    }

	private static DatePicker getDatePicker() {
    
    
		final DatePicker datepick;
		// 格式
        String DefaultFormat = "yyyy-MM-dd HH:mm:ss";
        // 当前时间
        Date date = new Date();
        // 字体
        Font font = new Font("Times New Roman", Font.BOLD, 14);
  
        Dimension dimension = new Dimension(177, 24);
  
        int[] hilightDays = {
    
     1, 3, 5, 7 };
  
        int[] disabledDays = {
    
     4, 6, 5, 9 };
  
        datepick = new DatePicker(date, DefaultFormat, font, dimension);
  
        datepick.setLocation(137, 83);
        datepick.setBounds(137, 83, 177, 24);
        // 设置一个月份中需要高亮显示的日子
        datepick.setHightlightdays(hilightDays, Color.red);
        // 设置一个月份中不需要的日子,呈灰色显示
        datepick.setDisableddays(disabledDays);
        // 设置国家
        datepick.setLocale(Locale.CHINA);
        // 设置时钟面板可见
        datepick.setTimePanleVisible(true);
		return datepick;
	}
}

10.2.JXDatePicker

本例使用 包swingx-core-1.6.5-1.jar,界面比较简约,可以设置日期

   <dependency>
            <groupId>org.swinglabs.swingx</groupId>
            <artifactId>swingx-core</artifactId>
            <version>1.6.5-1</version>
   </dependency>

10.3.时间控件练习:统计大于指定更新时间文件

借助时间控件,选中一个时间,然后根据这个时间,统计e:\project 修改时间大于这个时间的文件,一共有多少

扫描二维码关注公众号,回复: 13094470 查看本文章
import com.eltima.components.ui.DatePicker;
   
public class TestGUI {
    
    
    public static void main(String[] args) {
    
    
   
        JFrame f = new JFrame("LoL");
        f.setSize(500, 300);
        f.setLocation(200, 200);
        f.setLayout(null);
   
        final DatePicker datepick;
        datepick = getDatePicker();
   
        f.add(datepick);
   
        JButton b = new JButton("统计在D:/eclipse3.1 目录下,修改时间大于控件日期的文件总数");
        b.setBounds(20, 183, 450, 30);
        f.add(b);
   
        b.addActionListener(new ActionListener() {
    
    
   
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                File folder = new File("D:\\eclipse3.1");
                Date d = (Date) datepick.getValue();
                JOptionPane.showMessageDialog(f, "文件总数是:" + countFilesModifiedDateGreaterThan(folder, d));
            }
        });
   
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   
        f.setVisible(true);
    }
 
    private static DatePicker getDatePicker() {
    
    
        final DatePicker datepick;
        // 格式
        String DefaultFormat = "yyyy-MM-dd HH:mm:ss";
        // 当前时间
        Date date = new Date();
        // 字体
        Font font = new Font("Times New Roman", Font.BOLD, 14);
   
        Dimension dimension = new Dimension(177, 24);
   
        int[] hilightDays = {
    
     1, 3, 5, 7 };
   
        int[] disabledDays = {
    
     4, 6, 5, 9 };
   
        datepick = new DatePicker(date, DefaultFormat, font, dimension);
   
        // datepick.setLocation(137, 83);
        datepick.setBounds(137, 83, 177, 24);
        // 设置一个月份中需要高亮显示的日子
        datepick.setHightlightdays(hilightDays, Color.red);
        // 设置一个月份中不需要的日子,呈灰色显示
        datepick.setDisableddays(disabledDays);
        // 设置国家
        datepick.setLocale(Locale.CHINA);
        // 设置时钟面板可见
        datepick.setTimePanleVisible(true);
        return datepick;
    }
      
    //递归获取文件
    public static List<File> scanFolder(File folder){
    
    
        List<File> files = new ArrayList<>();
        File[] fs = folder.listFiles();
        for (File f : fs) {
    
    
            if(f.isDirectory()){
    
    
                 List<File> subFolderFiles = scanFolder(f);
                 files.addAll(subFolderFiles);
            }
            if(f.isFile()){
    
    
                files.add(f);
            }
        }
        return files;
    }
      
      //通过stream过滤文件修改时间大于指定时间的个数
    public static long countFilesModifiedDateGreaterThan(File folder,Date d){
    
    
        List<File> fs = scanFolder(folder);
        return fs.stream()
                .filter(f->f.lastModified()>d.getTime())
                .count();
    }
      
}

十一.表格

11.1.基本表格

显示一个Table需要两组数据

  • 一维数组: String[] columnName: 表示表格的标题
  • 二维数组: String[][] heros : 表格中的内容
  • 默认情况下,表格的标题不会显示,除非使用了JScrollPane
    在这里插入图片描述
    @Test
    public void JTable() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        // 表格上的title
        String[] columnNames = new String[]{
    
    "id", "name", "hp", "damage"};
        // 表格中的内容,是一个二维数组
        String[][] heros = new String[][]{
    
    {
    
    "1", "盖伦", "616", "100"}, {
    
    "2", "提莫", "512", "102"}, {
    
    "3", "奎因", "832", "200"}};

        JTable table = new JTable(heros, columnNames);

        frame.add(table, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

11.2.使用JScrollPane

JScrollPane: 带滚动条的Panel

  • 把table放进去就可以看到table的title

同样的把textarea放进去,并且textarea内容够长的话,就会看到滚动条
在这里插入图片描述

    @Test
    public void JTableShowTitle() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        // 表格上的title
        String[] columnNames = new String[]{
    
    "id", "name", "hp", "damage"};
        // 表格中的内容,是一个二维数组
        String[][] heros = new String[][]{
    
    {
    
    "1", "盖伦", "616", "100"}, {
    
    "2", "提莫", "512", "102"}, {
    
    "3", "奎因", "832", "200"}};

        JTable table = new JTable(heros, columnNames);

        // 根据t创建 JScrollPane
        JScrollPane scrollPane = new JScrollPane(table);

        //或则创建一个空的JScrollPane,再通过setViewportView把table放在JScrollPane中
        // JScrollPane sp = new JScrollPane(table);
        // sp.setViewportView(table);

        // 把sp而非JTable加入到JFrame上,
        frame.add(scrollPane, BorderLayout.CENTER);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        TimeUnit.SECONDS.sleep(1000);
    }

11.3.设置列宽

      // 设置列宽度
     table.getColumnModel().getColumn(0).setPreferredWidth(10);

11.4.TableModel

TableMode可以将表格的数据和显示分离

  • 比如对于JTable而言,有数据部分,也有显示部分(比如列宽等信息)。 数据部分可以声明一个叫做TableModel存放要显示的数据。

使用TableModel的方式存放Table需要显示的数据

需要继承AbstractTableModel,进而实现了接口TableModel,在实现类 中提供一个table显示需要的所有信息

  1. getRowCount 返回一共有多少行
  2. getColumnCount 返回一共有多少列
  3. getColumnName 每一列的名字
  4. isCellEditable 单元格是否可以修改
    5.getValueAt 每一个单元格里的值

当图形界面需要渲染第一个单元格的数据的时候,就会调用方法TabelModelgetValueAt(0,0),把返回值拿到并显示

在这里插入图片描述

public class HeroTableModel extends AbstractTableModel {
    
    

    private static final long serialVersionUID = -4796525201897896613L;
    String[] columnNames = new String[]{
    
    "id", "name", "hp", "damage"};
    String[][] heros = new String[][]{
    
    {
    
    "1", "盖伦", "616", "100"}, {
    
    "2", "提莫", "512", "102"}, {
    
    "3", "奎因", "832", "200"}};

    // 返回一共有多少行
    @Override
    public int getRowCount() {
    
    
        return heros.length;
    }

    // 返回一共有多少列
    @Override
    public int getColumnCount() {
    
    
        return columnNames.length;
    }
    // 获取每一列的名称
    @Override
    public String getColumnName(int columnIndex) {
    
    
        return columnNames[columnIndex];
    }
    // 单元格是否可以修改
    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
    
    
        return false;
    }
    // 每一个单元格里的值
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
    
    
        return heros[rowIndex][columnIndex];
    }

    /**
     * 自定义TableModel
     * @throws InterruptedException
     */
    @Test
    public void customTableModel() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        //创建一个TableModel
        HeroTableModel htm = new HeroTableModel();

        //根据 TableModel来创建 Table
        JTable t = new JTable(htm);

        JScrollPane sp = new JScrollPane(t);

        frame.add(sp, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);

        TimeUnit.SECONDS.sleep(1000);
    }
}

也可以直接通过JTable的构造方法传入标题以及数据创建表格

JTable(Object[][] rowData, Object[] columnNames) 

11.5.TableModel 与 数据库结合(使用List模拟数据库操作)

只需要修改HeroTableModel,引入Dao层,无需修改展示。 这正好演绎了Model设计思想中的数据分离的好处,当只需要数据发生变化的时候,修改Model即可,界面GUI部分,不需要做任何改动

由于时间关系 下面的代码都是使用内存来模拟数据库的增删改查, 实际上操作数据库也只是把 HeroDAO代码改为操作数据库代码而已

public class HeroTableModelDB extends AbstractTableModel {
    
    

    private static final long serialVersionUID = -4796525201897896613L;
    // 使用从DAO返回的List作为TableModel的数据
    public List<Hero> heros = new HeroDAO().list(0,10);
    public String[] columnNames = new String[]{
    
    "id", "name", "hp", "damage"};

    // heros.size返回一共有多少行
    @Override
    public int getRowCount() {
    
    
        return heros.size();
    }

    @Override
    public int getColumnCount() {
    
    
        return columnNames.length;
    }

    @Override
    public String getColumnName(int columnIndex) {
    
    
        return columnNames[columnIndex];
    }

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

    // 先通过heros.get(rowIndex)获取行对应的Hero对象
    // 然后根据columnIndex返回对应的属性
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
    
    
        Hero h = heros.get(rowIndex);
        if (0 == columnIndex) {
    
    
            return h.id;
        }
        if (1 == columnIndex) {
    
    
            return h.name;
        }
        if (2 == columnIndex) {
    
    
            return h.hp;
        }
        if (3 == columnIndex) {
    
    
            return h.damage;
        }
        return null;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Hero {
    
    
        private String id;
        private String name;
        private String hp;
        private String damage;
    }

	//静态内部类HeroDAO,通过操作内存来模拟对数据库的增删改查
    static class HeroDAO {
    
    
    	//保存模拟数据容器
        public static List<Hero> heroList = new ArrayList<>();
		//模拟数据
        static {
    
    
            Hero h1 = new Hero("1", "盖伦", "616", "100");
            Hero h2 = new Hero("2", "提莫", "512", "102");
            Hero h3 = new Hero("3", "奎因", "832", "200");
            Hero h4 = new Hero("4", "后羿", "822", "500");
			Hero h5 = new Hero("5", "盖伦", "616", "100");
			Hero h6 = new Hero("6", "提莫", "512", "102");
			Hero h7 = new Hero("7", "奎因", "832", "200");
			Hero h8 = new Hero("8", "后羿", "822", "500");
			Hero h9 = new Hero("9", "盖伦", "616", "100");
			Hero h10 = new Hero("10", "提莫", "512", "102");
			Hero h11 = new Hero("11", "奎因", "832", "200");
			Hero h12 = new Hero("12", "后羿", "822", "500");

            heroList.add(h1);
            heroList.add(h2);
            heroList.add(h3);
            heroList.add(h4);
			heroList.add(h5);
			heroList.add(h6);
			heroList.add(h7);
			heroList.add(h8);
			heroList.add(h9);
			heroList.add(h10);
			heroList.add(h11);
			heroList.add(h12);

        }

        /**
         * 模拟查询数据库方法
         *
         * @return
         */
        public List<Hero> list() {
    
    
            return heroList;
        }
        /**
         * 模拟查询数据库方法
         * @return
         */
        public List<Hero> list(int start, int number) {
    
    
            return page(heroList,start,number);
        }
        /**
         * 模拟新增
         * @return
         */
        public void add(Hero hero) {
    
    
            heroList.add(0, hero);
        }
        /**
         * 根据id删除
         * @param id
         */
        public void delete(String id) {
    
    
            heroList.removeIf(hero -> StringUtils.equals(id, hero.getId()));
        }
        /**
         * 获取数据总量
         * @return
         */
        public int getTotal() {
    
    
            return heroList.size();
        }
        /**
         * 根据id更新数据
         * @param hero
         */
        public void update(Hero hero) {
    
    
            heroList.forEach(e -> {
    
    
                if (StringUtils.equals(e.getId(), hero.getId())) {
    
    
                    e.setName(hero.getName());
                    e.setHp(hero.getHp());
                    e.setDamage(hero.getDamage());
                }
            });
        }
		/**
		 * 循环截取某页列表进行分页
		 * @param dataList 分页数据
		 * @param pageSize  页面大小
		 * @param currentPage   当前页面
		 */
		public static List<Hero> page(List<Hero> dataList,int currentPage, int pageSize ) {
    
    
			List<Hero> currentPageList = new ArrayList<>();
			if (dataList != null && dataList.size() > 0) {
    
    
				int currIdx = (currentPage > 1 ? (currentPage - 1) * pageSize : 0);
				for (int i = 0; i < pageSize && i < dataList.size() - currIdx; i++) {
    
    
					Hero data = dataList.get(currIdx + i);
					currentPageList.add(data);
				}
			}
			return currentPageList;
		}
    }
}

11.5.1.测试展示数据库内容

    @Test
    public void customTableModelDB() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        //创建一个TableModel
        HeroTableModelDB htm = new HeroTableModelDB();

        //根据 TableModel来创建 Table
        JTable table = new JTable(htm);

        JScrollPane sp = new JScrollPane(table);

        frame.add(sp, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

在这里插入图片描述

11.5.2.监听table选中项的变化

通过table可以获取一个 TableSelectionModel,专门用于监听jtable选中项的变化
在这里插入图片描述

    @Test
    public void getSelectionModel() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        final HeroTableModelDB htm = new HeroTableModelDB();

        final JTable table = new JTable(htm);
        // 准备一个Panel上面放一个Label用于显示哪条被选中了
        JPanel panel = new JPanel();
        final JLabel label = new JLabel("暂时未选中条目");
        panel.add(label);

        JScrollPane sp = new JScrollPane(table);

        // 使用selection监听器来监听table的哪个条目被选中
        table.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
    
    

                    // 当选择了某一行的时候触发该事件
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
    
    
                        // 获取哪一行被选中了
                        int row = table.getSelectedRow();
                        // 根据选中的行,到HeroTableModel中获取对应的对象
                        HeroTableModelDB.Hero h = htm.heros.get(row);
                        // 更新标签内容
                        label.setText("当前选中的英雄是: " + h.getName());

                    }
                });

        frame.add(panel, BorderLayout.NORTH);
        frame.add(sp, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

11.5.3.输入项校验

如果用户输入的名称为空,或者血量不是小数,在提交数据的时候弹出提示。
在这里插入图片描述

    @Test
    public void checkTable() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        final HeroTableModelDB htm = new HeroTableModelDB();

        final JTable table = new JTable(htm);
        // 增加 一个 panel用于放置名称,血量输入框和增加 按钮
        JPanel panel = new JPanel();

        final JLabel lName = new JLabel("名称");
        final JTextField tfName = new JTextField("");
        final JLabel lHp = new JLabel("血量");
        final JTextField tfHp = new JTextField("");
        JButton bAdd = new JButton("增加");
        tfName.setPreferredSize(new Dimension(80, 30));
        tfHp.setPreferredSize(new Dimension(80, 30));

        panel.add(lName);
        panel.add(tfName);
        panel.add(lHp);
        panel.add(tfHp);
        panel.add(bAdd);

        // 为增加按钮添加监听
        bAdd.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    

                HeroDAO heroDAO = new HeroDAO();

                String name = tfName.getText();
                // 通过name长度判断 名称是否为空
                if (name.length() == 0) {
    
    
                    // 弹出对话框提示用户
                    JOptionPane.showMessageDialog(frame, "名称不能为空");
                    // 名称输入框获取焦点
                    tfName.grabFocus();
                    return;
                }

                String hp = tfHp.getText().trim();
                try {
    
    
                    // 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式
                    Float.parseFloat(hp);
                } catch (NumberFormatException e1) {
    
    
                    JOptionPane.showMessageDialog(frame, "血量只能是小数 ");
                    tfHp.grabFocus();
                    return;
                }
               
                // 根据输入框数据创建一个Hero对象
                Hero hero = new Hero();
                hero.setName(name);
                hero.setHp(hp);
                hero.setId(System.currentTimeMillis() + "");
                hero.setDamage("0");

                // 通过dao把该对象加入到数据库
                heroDAO.add(hero);
                // 通过dao更新tablemodel中的数据
                htm.heros = heroDAO.list();
                // 调用JTable的updateUI,刷新界面。
                // 刷新界面的时候,会到tableModel中去取最新的数据
                // 就能看到新加进去的数据了
                table.updateUI();
            }
        });

        JScrollPane scrollPane = new JScrollPane(table);

        frame.add(panel, BorderLayout.NORTH);
        frame.add(scrollPane, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

11.5.4.选中指定行

  • table初始化后,默认选中第一行
  • 新增数据后,默认选中新增的这一条
    在这里插入图片描述
    @Test
    public void selectLine() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new BorderLayout());

        final HeroTableModelDB htm = new HeroTableModelDB();

        final JTable table = new JTable(htm);
        // 设置选择模式为 只能选中一行
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // 选中第一行 (基本0)
        table.getSelectionModel().setSelectionInterval(0, 0);

        // 增加 一个 panel用于放置名称,血量输入框和增加 按钮
        JPanel panel = new JPanel();

        final JLabel lName = new JLabel("名称");
        final JTextField tfName = new JTextField("");
        final JLabel lHp = new JLabel("血量");
        final JTextField tfHp = new JTextField("");
        JButton bAdd = new JButton("增加");
        tfName.setPreferredSize(new Dimension(80, 30));
        tfHp.setPreferredSize(new Dimension(80, 30));

        panel.add(lName);
        panel.add(tfName);
        panel.add(lHp);
        panel.add(tfHp);
        panel.add(bAdd);

        // 为增加按钮添加监听
        bAdd.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    

                HeroDAO heroDAO = new HeroDAO();

                String name = tfName.getText();
                // 通过name长度判断 名称是否为空
                if (name.length() == 0) {
    
    
                    // 弹出对话框提示用户
                    JOptionPane.showMessageDialog(frame, "名称不能为空");
                    // 名称输入框获取焦点
                    tfName.grabFocus();
                    return;
                }

                String hp = tfHp.getText().trim();
                try {
    
    
                    // 把hp转换为浮点型,如果出现异常NumberFormatException表示不是浮点型格式
                    Float.parseFloat(hp);
                } catch (NumberFormatException e1) {
    
    
                    JOptionPane.showMessageDialog(frame, "血量只能是小数 ");
                    tfHp.grabFocus();
                    return;
                }

                // 根据输入框数据创建一个Hero对象
                Hero hero = new Hero();
                hero.setName(name);
                hero.setHp(hp);
                hero.setId(System.currentTimeMillis() + "");
                hero.setDamage("0");

                // 通过dao把该对象加入到数据库
                heroDAO.add(hero);

                // 通过dao更新tablemodel中的数据
                htm.heros = heroDAO.list();

                // 调用JTable的updateUI,刷新界面。
                // 刷新界面的时候,会到tableModel中去取最新的数据
                // 就能看到新加进去的数据了
                table.updateUI();

                // 选中 第一行 ,因为 DAO是按照 ID倒排序查询,所以第一行就是新加入的数据
                table.getSelectionModel().setSelectionInterval(0, 0);
            }
        });

        JScrollPane scrollPane = new JScrollPane(table);

        frame.add(panel, BorderLayout.NORTH);
        frame.add(scrollPane, BorderLayout.CENTER);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);

        TimeUnit.SECONDS.sleep(1000);
    }

11.6.练习:表格增删改查(使用List模拟数据库操作)

在这里插入图片描述

public class TableCrudDemo {
    
    

    static HeroTableModelDB htm = new HeroTableModelDB();
    static JTable table = new JTable(htm);

    // 把分页按钮放在这里,后面监听器好访问
    static JButton bFirst = new JButton("首页");
    static JButton bPre = new JButton("上一页");
    static JButton bNext = new JButton("下一页");
    static JButton bLast = new JButton("末页");
    static JComboBox<Integer> goPageCheckBox = new JComboBox<>();//跳转页数

    static int pageSize = 10;// 每页显示10个
    static int pageNum = 1;// 开始的页码
    private static boolean cbListenerEnabled = true;

    public static void main(String[] args) {
    
    
        //WebLookAndFeel.install ();

        final JFrame frame = new JFrame("LoL");
        frame.setSize(400, 340);
        frame.setLocation(200, 200);

        //默认选中第0行
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.getSelectionModel().setSelectionInterval(0, 0);

        JPanel pOperation = new JPanel();

        JButton bAdd = new JButton("增加");
        JButton bDelete = new JButton("删除");
        JButton bEdit = new JButton("编辑");
        pOperation.add(bAdd);
        pOperation.add(bDelete);
        pOperation.add(bEdit);

        JPanel pPage = new JPanel();

        pPage.add(bFirst);
        pPage.add(bPre);
        pPage.add(goPageCheckBox);

        pPage.add(bNext);
        pPage.add(bLast);

        //新增按钮事件
        addBtnListener(frame, bAdd);
        //编辑按钮事件
        editBtnListener(frame, bEdit);
        //删除按钮事件
        deleteBtnListener(frame, bDelete);
        //分页按钮事件
        pageBtnListener();
        //跳转按钮事件
        goPageBtnListener(goPageCheckBox);

        JScrollPane scrollPane = new JScrollPane(table);
        frame.setLayout(null);
        scrollPane.setBounds(0, 0, 394, 200);
        pOperation.setBounds(0, 200, 394, 50);
        pPage.setBounds(0, 250, 394, 200);
        frame.add(scrollPane);
        frame.add(pOperation);
        frame.add(pPage);
        updateButtonStatus();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setVisible(true);
    }


    /**
     * 跳转按钮事件
     */
    private static void goPageBtnListener(JComboBox<Integer> goPageCheckBox) {
    
    
        //跳转按钮事件
        goPageCheckBox.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                if (!cbListenerEnabled) {
    
    
                    return;
                }

                int currentPage = (int) goPageCheckBox.getSelectedItem();
                pageNum = currentPage;//当前页数=跳转页数
                updateTable();
                updateButtonStatus();

            }
        });
    }

    /**
     * 新增按钮事件
     */
    private static void addBtnListener(JFrame frame, JButton bAdd) {
    
    
        bAdd.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                new AddDialog(frame).setVisible(true);

                updateButtonStatus();
            }
        });
    }

    /**
     * 编辑按钮事件
     */
    private static void editBtnListener(JFrame frame, JButton bEdit) {
    
    
        bEdit.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                // 判断是否选中
                int index = table.getSelectedRow();
                if (-1 == index) {
    
    
                    JOptionPane.showMessageDialog(frame, "编辑前需要先选中一行");
                    return;
                }

                // 获取选中的对象
                HeroTableModelDB.Hero hero = htm.heros.get(index);

                // 显示编辑Dialog

                EditDialog ed = new EditDialog(frame);
                ed.tfName.setText(hero.getName());
                ed.tfHp.setText(hero.getHp());

                ed.setVisible(true);

            }
        });
    }


    /**
     * 删除按钮事件
     */
    private static void deleteBtnListener(JFrame frame, JButton bDelete) {
    
    
        bDelete.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    

                // 判断是否选中
                int index = table.getSelectedRow();
                if (-1 == index) {
    
    
                    JOptionPane.showMessageDialog(frame, "删除前需要先选中一行");
                    return;
                }

                // 进行确认是否要删除
                if (JOptionPane.OK_OPTION != JOptionPane.showConfirmDialog(frame, "确认要删除?")) {
    
    
                    return;
                }

                // 获取id
                HeroTableModelDB.Hero hero = htm.heros.get(index);
                String id = hero.getId();

                // 删除
                new HeroTableModelDB.HeroDAO().delete(id);

                // 更新table
                pageNum = 1;
                updateTable();
                updateButtonStatus();

            }
        });
    }


    /**
     * 分页按钮事件
     */
    private static void pageBtnListener() {
    
    

        bFirst.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                pageNum = 1;
                updateTable();
                updateButtonStatus();
            }
        });
        bPre.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                pageNum = pageNum - 1;
                updateTable();
                updateButtonStatus();
            }
        });
        bNext.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                pageNum = pageNum + 1;
                updateTable();
                updateButtonStatus();
            }
        });
        bLast.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    

                pageNum = calcTotalPage();

                updateTable();

                updateButtonStatus();

            }

        });

    }

    /**
     * 更新按钮状态
     */
    private static void updateButtonStatus() {
    
    
        int totalPage = calcTotalPage();

        // 是否有上一页
        if (1 != pageNum) {
    
    
            bFirst.setEnabled(true);
            bPre.setEnabled(true);
        }

        // 是否是第一页
        if (1 == pageNum) {
    
    
            bFirst.setEnabled(false);
            bPre.setEnabled(false);
        }

        // 是否是最后一页
        if (pageNum == totalPage) {
    
    
            bLast.setEnabled(false);
            bNext.setEnabled(false);
        }
        // 是否有下一页
        if (pageNum < totalPage) {
    
    
            bLast.setEnabled(true);
            bNext.setEnabled(true);
        }

        cbListenerEnabled = false;
        goPageCheckBox.removeAllItems();

        //总共的页数
        for (int i = 0; i < totalPage; i++) {
    
    
            goPageCheckBox.addItem(i + 1);
        }
        cbListenerEnabled = true;
        goPageCheckBox.setSelectedItem(pageNum);
    }

    /**
     * 根据表格数据
     */
    public static void updateTable() {
    
    
        htm.heros = new HeroTableModelDB.HeroDAO().list(pageNum, pageSize);
        table.updateUI();
        if (!htm.heros.isEmpty()) {
    
    
            table.getSelectionModel().setSelectionInterval(0, 0);
        }
    }

    /**
     * 检查输入框空
     *
     * @param tf
     * @param msg
     * @return
     */
    private static boolean checkEmpty(JTextField tf, String msg) {
    
    
        String value = tf.getText();
        if (0 == value.length()) {
    
    
            JOptionPane.showMessageDialog(null, msg + " 不能为空");
            tf.grabFocus();
            return false;
        }
        return true;
    }

    /**
     * 检查输入框空与非数字
     *
     * @param tf
     * @param msg
     * @return
     */
    private static boolean checkNumber(JTextField tf, String msg) {
    
    
        String value = tf.getText();
        if (0 == value.length()) {
    
    
            JOptionPane.showMessageDialog(null, msg + " 不能为空");
            tf.grabFocus();
            return false;
        }
        try {
    
    
            Integer.parseInt(value);
        } catch (NumberFormatException e) {
    
    
            JOptionPane.showMessageDialog(null, msg + " 只能是整数");
            tf.grabFocus();
            return false;
        }

        return true;
    }

    /**
     * 计算总页数
     *
     * @return
     */
    private static int calcTotalPage() {
    
    
        int total = new HeroTableModelDB.HeroDAO().getTotal();
        int last = total % pageSize == 0 ? total / pageSize : (total / pageSize) + 1;
        return last;

    }

    /**
     * 新增对话框
     */
    static class AddDialog extends JDialog {
    
    
        JLabel lName = new JLabel("名称");
        JLabel lHp = new JLabel("血量");

        JTextField tfName = new JTextField();
        JTextField tfHp = new JTextField();

        JButton bSubmit = new JButton("提交");

        AddDialog(JFrame frame) {
    
    
            super(frame);
            this.setModal(true);
            int gap = 50;
            this.setLayout(null);

            JPanel pInput = new JPanel();
            JPanel pSubmit = new JPanel();

            pInput.setLayout(new GridLayout(2, 2, gap, gap));
            pInput.add(lName);
            pInput.add(tfName);
            pInput.add(lHp);
            pInput.add(tfHp);

            pSubmit.add(bSubmit);

            pInput.setBounds(50, 20, 200, 100);
            pSubmit.setBounds(0, 130, 300, 150);

            this.add(pInput);
            this.add(pSubmit);

            this.setSize(300, 200);
            this.setLocationRelativeTo(frame);
            bSubmit.addActionListener(new ActionListener() {
    
    

                @Override
                public void actionPerformed(ActionEvent e) {
    
    
                    if (checkEmpty(tfName, "名称")) {
    
    
                        if (checkNumber(tfHp, "hp")) {
    
    

                            String name = tfName.getText();
                            String hp = tfHp.getText();

                            HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();
                            h.setName(name);
                            h.setHp(hp);
                            h.setId(System.currentTimeMillis() + "");
                            h.setDamage("0");

                            new HeroTableModelDB.HeroDAO().add(h);

                            JOptionPane.showMessageDialog(frame, "提交成功 ");

                            AddDialog.this.setVisible(false);
                            pageNum = 1;
                            updateTable();
                        }
                    }

                }
            });

        }
    }

    /**
     * 编辑对话框
     */
    static class EditDialog extends JDialog {
    
    
        JLabel lName = new JLabel("名称");
        JLabel lHp = new JLabel("血量");

        JTextField tfName = new JTextField();
        JTextField tfHp = new JTextField();

        JButton bSubmit = new JButton("提交");

        EditDialog(JFrame frame) {
    
    
            super(frame);
            this.setModal(true);
            int gap = 50;
            this.setLayout(null);

            JPanel pInput = new JPanel();
            JPanel pSubmit = new JPanel();

            pInput.setLayout(new GridLayout(2, 2, gap, gap));
            pInput.add(lName);
            pInput.add(tfName);
            pInput.add(lHp);
            pInput.add(tfHp);

            pSubmit.add(bSubmit);

            pInput.setBounds(50, 20, 200, 100);
            pSubmit.setBounds(0, 130, 300, 150);

            this.add(pInput);
            this.add(pSubmit);

            this.setSize(300, 200);
            this.setLocationRelativeTo(frame);

            bSubmit.addActionListener(new ActionListener() {
    
    

                @Override
                public void actionPerformed(ActionEvent e) {
    
    
                    if (checkEmpty(tfName, "名称")) {
    
    
                        if (checkNumber(tfHp, "hp")) {
    
    

                            // 获取id
                            int index = table.getSelectedRow();
                            String id = htm.heros.get(index).getId();

                            String name = tfName.getText();
                            String hp = tfHp.getText();

                            HeroTableModelDB.Hero h = new HeroTableModelDB.Hero();
                            h.setName(name);
                            h.setHp(hp);
                            h.setId(id);
                            h.setDamage("0");

                            new HeroTableModelDB.HeroDAO().update(h);

                            JOptionPane.showMessageDialog(frame, "提交成功 ");

                            EditDialog.this.setVisible(false);
                            updateTable();
                        }
                    }

                }
            });
        }
    }
}

十二.Swing中的线程

12.1.三种线程

在Swing程序的开发中,有3种线程的概念

  1. 初始化线程
    用于创建各种容器,组件并显示他们,一旦创建并显示,初始化线程的任务就结束了。

  2. 事件调度线程
    Swing是一个事件驱动的模型,所有和事件相关的操作都放是放在事件调度线程 (Event Dispatch)中进行的。比如按钮的点击时间,ActionListener.actionPerformed中的代码,就是在事件调度线程 Event Dispatch Thread中执行的。

  3. 长耗时任务线程
    有时候需要进行一些长时间的操作,比如访问数据库,文件复制,连接网络,统计文件总数等等。 这些操作就不适合放在事件调度线程中进行,因为占用时间久了,会让使用者感觉界面响应很卡顿。 为了保持界面响应的流畅性,所有长耗时任务都应该放在专门的 长耗时任务线程中进行

12.2.事件调度线程是单线程的

为什么 事件调度线程是单线程的呢?

因为 Swing的各种组件,比如JTextField,JButton 都不是线程安全的,这就意味着,如果有多个线程,那么同一个JTextField的setText方法,可能会被多个线程同时调用,这会导致线程安全问题)的发生。

  • 如果把组件类设计成为线程安全的,由于Swing事件调度的复杂性,就很有可能导致死锁的发生。

为了规避同步问题,以及降低整个Swing设计的复杂度,提高Swing的相应速度,Swing中的 事件调度线程被设计成为了单线程模式,即只有一个线程在负责事件的响应工作。

12.3.初始化线程

如代码所示,我们初始化一个图形界面的时候,都会直接在主方法的主线程里,直接调用如下代码来进行初始化

new TestFrame().setVisible(true);

如果是简单程序这没有什么问题,如果是复杂的程序就有可能产生问题了。

  • 因为这里有两个线程在同时访问组件:1. 主线程 2. 事件调度线程。 如果是复杂的图形界面程序,就有可能出现这两个线程同时操作的情况,导致同步问题的产生。

为了规避这个问题的产生,创建和显示界面的工作,最好也交给事件调度线程,这样就保证了只有一个线程在访问这些组件

 
public class TestGUI {
    
    
    public static void main(String[] args) {
    
    
        //交给主线程创建和显示界面
       // new TestFrame().setVisible(true);
       
       //交给事件调度线程创建和显示界面
       SwingUtilities.invokeLater(new Runnable() {
    
    
          public void run() {
    
    
              new TestFrame().setVisible(true);
          }
      });
    }
 
    static class TestFrame extends JFrame {
    
    
        public TestFrame() {
    
    
            setTitle("LoL");
            setSize(400, 300);
            setLocation(200, 200);
            setLayout(null);
 
            JButton btn = new JButton("一键秒对方基地挂");
            btn .setBounds(50, 50, 280, 30);
            add(btn );
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
             
            System.out.println("当前线程是否是 事件调度线程: " + SwingUtilities.isEventDispatchThread());
 
        }
    }
}

12.4.事件调度线程

以 按钮监听 中的代码为例,ActionListener.actionPerformed 中的代码,就是事件调度线程执行的。

可以借助SwingUtilities.isEventDispatchThread() 判断是否是事件调度线程

    @Test
    public void eventDiapatcherThread() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(400, 300);
        frame.setLocation(580, 200);
        frame.setLayout(null);

        final JLabel label = new JLabel();

        ImageIcon i = new ImageIcon("E:\\data\\gareen.jpg");
        label.setIcon(i);
        label.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());

        JButton b = new JButton("隐藏图片");
        b.setBounds(150, 200, 100, 30);

        b.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                label.setVisible(false);

                System.out.println("当前使用的是事件调度线程:" + SwingUtilities.isEventDispatchThread());
            }
        });

        frame.add(label);
        frame.add(b);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        
        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

点击按钮
在这里插入图片描述

在这里插入图片描述

12.5.长耗时任务线程

有时候需要执行长耗时任务,比如数据库查询,文件复制,访问网络等。

  • 这些操作一般都会在事件响应后发起,就会自动进入事件调度线程。 而事件调度线程又是单线程模式,其结果就会是在执行这些长耗时任务的时候,界面就无响应了

  • 如图所示,当点击第一个按钮的时候,会在其中进行一个5秒钟的任务,这个期间,第一个按钮会保持按下状态,其他按钮也无法点击,出现了无响应了状态。
    在这里插入图片描述

为了解决这个问题,Swing提供了一个SwingWorker类来解决。SwingWorker是一个抽象类,为了使用,必须实现方法 doInBackground,在doInBackground中编写我们的任务,然后执行SwingWorker的execute方法,放在专门的工作线程中去运行。

    @Test
    public void longConsumingTask() throws InterruptedException {
    
    
        JFrame frame = new JFrame("LoL");
        frame.setSize(300, 300);
        frame.setLocation(200, 200);
        frame.setLayout(new FlowLayout());

        JButton b1 = new JButton("在事件调度线程中执行长耗时任务");
        JButton b2 = new JButton("使用SwingWorker执行长耗时任务");
        JLabel label = new JLabel("任务执行结果");
        frame.add(b1);
        frame.add(b2);
        frame.add(label);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        b1.addActionListener(new ActionListener() {
    
    

            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                label.setText("开始执行完毕");
                try {
    
    
                    Thread.sleep(5000);
                } catch (InterruptedException e1) {
    
    
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                label.setText("任务执行完毕");
            }
        });

        b2.addActionListener(new ActionListener() {
    
    
            @Override
            public void actionPerformed(ActionEvent e) {
    
    
                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
    
    
                    @Override
                    protected Void doInBackground() throws Exception {
    
    
                        System.out.println("执行这个SwingWorder的线程是:" + Thread.currentThread().getName());
                        label.setText("开始执行完毕");
                        try {
    
    
                            Thread.sleep(5000);
                        } catch (InterruptedException e1) {
    
    
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        }
                        label.setText("任务执行完毕");
                        return null;
                    }
                };
                worker.execute();

            }
        });
        frame.setVisible(true);

        //设置休眠时间,是为了延缓主线程销毁
        TimeUnit.SECONDS.sleep(1000);
    }

在这里插入图片描述
在这里插入图片描述

12.6.练习:搜索指定目录下的所有Java文件

在这里插入图片描述

public class SearchJavaFile {
    
    

    private static int FOUND_COUNT = 0;

    public static void main(String[] args) {
    
    
        // 为了规避线程安全问题的产生,创建和显示界面的工作,使用调度线程初始化窗口,这样就保证了只有一个线程在访问这些组件
        SwingUtilities.invokeLater(() -> {
    
    
                    WebLookAndFeel.install();
                    new SearchFrame().setVisible(true);
                }
        );
    }

    /**
     * 递归搜索文件内容,是否包含指定关键字文件个数
     *
     * @param file   目录
     * @param search 关键字
     * @param suffix 文件后缀
     */
    public static void search(File file, String search, String suffix) {
    
    
        if (file.isFile()) {
    
    
            if (file.getName().toLowerCase().endsWith(suffix)) {
    
    
                String fileContent = readFileConent(file);
                if (fileContent.contains(search)) {
    
    
                    System.out.println(fileContent);
                    FOUND_COUNT++;
                }
            }
        }
        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles();
            for (File f : files) {
    
    
                search(f, search, suffix);
            }
        }
    }


    /**
     * 递归搜索文件内容,是否包含指定关键字文件个数
     *
     * @param file   目录
     * @param search 关键字
     * @param suffix 文件后缀
     */
    public static int searchPlus(File file, String search, String suffix) {
    
    
        int foundCount = 0;
        if (file.isFile()) {
    
    
            if (file.getName().toLowerCase().endsWith(suffix)) {
    
    
                String fileContent = readFileConent(file);
                if (fileContent.contains(search)) {
    
    
                    System.out.println(fileContent);
                    foundCount += 1;
                }
            }
        }
        if (file.isDirectory()) {
    
    
            File[] files = file.listFiles();
            for (File f : files) {
    
    
                foundCount += searchPlus(f, search, suffix);
            }
        }

        return foundCount;
    }

    /**
     * 读取文件文本内容
     *
     * @param file
     * @return
     */
    public static String readFileConent(File file) {
    
    
        try (FileReader fr = new FileReader(file)) {
    
    
            char[] all = new char[(int) file.length()];
            fr.read(all);
            return new String(all);
        } catch (IOException e) {
    
    
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查询窗口初始化
     */
    static class SearchFrame extends JFrame {
    
    
        JLabel lLocation = new JLabel("查询目录");
        JLabel lSearch = new JLabel("文件内容");

        JTextField tfLocation = new JTextField("e:/data");
        JTextField tfSearch = new JTextField("g");

        JButton bSubmit = new JButton("搜索");


        /**
         * 容器初始化
         */
        SearchFrame() {
    
    
            int gap = 50;
            this.setLayout(null);

            JPanel pInput = new JPanel();
            JPanel pSubmit = new JPanel();

            pInput.setLayout(new GridLayout(2, 2, gap, gap));
            pInput.add(lLocation);
            pInput.add(tfLocation);
            pInput.add(lSearch);
            pInput.add(tfSearch);

            pSubmit.add(bSubmit);

            pInput.setBounds(50, 20, 200, 100);
            pSubmit.setBounds(0, 130, 300, 150);

            this.add(pInput);
            this.add(pSubmit);

            this.setSize(300, 200);
            this.setLocationRelativeTo(null);
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            bSubmit.addActionListener(new ActionListener() {
    
    
                @Override
                public void actionPerformed(ActionEvent e) {
    
    
                    FOUND_COUNT = 0;
                    String location = tfLocation.getText();
                    String search = tfSearch.getText();
                    if (0 == location.length()) {
    
    
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
                        tfLocation.grabFocus();
                        return;
                    }
                    if (0 == search.length()) {
    
    
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不能为空");
                        tfSearch.grabFocus();
                        return;
                    }

                    File folder = new File(location);
                    if (!folder.exists()) {
    
    
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不存在");
                        tfLocation.grabFocus();
                        return;
                    }
                    if (!folder.isDirectory()) {
    
    
                        JOptionPane.showMessageDialog(SearchFrame.this, "搜索目录不是一个文件夹");
                        tfLocation.grabFocus();
                        return;
                    }

                    //正在搜索按钮
                    freeze();

                    SwingWorker worker = new SwingWorker() {
    
    
                        @Override
                        protected Object doInBackground() throws Exception {
    
    
                            //work here
                            int foundCount = searchPlus(folder, search, ".java");
                            JOptionPane.showMessageDialog(SearchFrame.this, "总共找到满足条件的文件: " + foundCount + " 个");
                            //搜索按钮
                            unfreeze();
                            return null;

                        }

                    };

                    worker.execute();
                }
            });
        }

        /**
         * 搜索按钮
         */
        private void unfreeze() {
    
    
            bSubmit.setText("搜索");
            bSubmit.setEnabled(true);
            tfLocation.setEnabled(true);
            tfSearch.setEnabled(true);

        }

        /**
         * 正在搜索按钮
         */
        private void freeze() {
    
    
            bSubmit.setText("正在搜索");
            bSubmit.setEnabled(false);
            tfLocation.setEnabled(false);
            tfSearch.setEnabled(false);
        }
    }
}

十三.界面风格

Java提供了非常便捷的方式切换界面风格

引入依赖

        <dependency>
            <groupId>com.weblookandfeel</groupId>
            <artifactId>weblaf-ui</artifactId>
            <version>1.2.13</version>
        </dependency>

加入这一行代码就行了

 WebLookAndFeel.install();

使用前:
在这里插入图片描述

使用后:
在这里插入图片描述

网上还有很多Swing风格包,大家可以自己去网上搜索

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/109994194