Java Swing-4.样式,边距及布局管理器的灵活使用

环境

jdk17(jdk14 以后自带将jar 打成安装包工具,版本从1.8调整到17)

Maven:3.2.5

pom.xml:在上篇文章中《Java Swing-2.环境搭建及窗口背景

效果

下一章:如何通过jdk 自带的工具 打包成 exe ,msi ,或者面安装版的exe 文件(自带jre环境),无需安装这安装jdk

  • exe4j: 免费版会有弹框 提示用户使用的免费版exe4j打包工具
  • Launch4j: 开源,但目前没找到如何把jre 环境直接放到exe 中,需要把exe和jre 打成一个压缩包形式使用

说明

Swing 控制样式总体来说分为两种

  • 通过布局管理器
  • 组件提供的修改样式方法(部分组件,有限调整)
  • 继承组件后通过画布 重写绘制该组件(按钮变圆,背景色等)

1.布局管理器

布局管理器之前要先了解Swing AWT 的容器(窗口)概念,因为布局管理器是针对容器的,每个容器可以设置一个属于自己的布局管理器。基本概念在这;同时容器可以添加其他容器,组件。

一个容器的整体分布、布局方式。

FlowLayout

流式布局,依次排序,遇到边界挪至下一行

BorderLayout

边界布局(区域布局),分为北、南、西、东、中(上、下、左、右、中) 五个区域

GridLayout

纵横风格的网络,每个网格所占区域大小相同。默认从左向右

GridBagLayout

功能最强大的布局管理器,纵横网格,可以跨越一个或多个网格,设置网格的大小(类似Excel中的合并单元格 ,上下 左右合并)比较难控制

CardLayout

将加入容器的租价看成一叠卡片,只有最上层的才可见(类似轮播图)

绝对定位

setLayout(null)

设置后不会再自动填充这个容器,

BoxLayout

GridBagLayout布局管理器的简化版,配合 Box 一起使用,如果想要简单控制组件间距,可以使用该组件

组件方法

不是所有组件都有以下方法。

.setBounds();
java 复制代码
// x起点,y起点 宽,高
button.setBounds(5, 5, 80, 25);
.setFont();
java 复制代码
// 设置字体 形状,大小
zlabel.setFont(new Font("Serif", Font.BOLD, 36));
.setSize();
java 复制代码
// 宽,高
        newLabel.setSize(10,20);
.setIcon();
java 复制代码
// 设置图标(按钮图标,树状图图标)
        newLabel.setIcon(new ImageIcon("/path/xxx/ccc"));
        // 设置背景色(边框)
        jPanel.setBackground(Color.black);

继承重绘组件

主要依赖于以下两种画布

  • Graphics
    继承组件 重新 paintComponent(Graphics g) 方法
  • Canvase

主要代码

项目目录

控制样式部分都在 首页及编辑页面,主要使用了布局管理器,组件方法来控制按钮的位置

首页代码

java 复制代码
package com.tiger.four;

import com.tiger.four.table.GoodsJTable;
import com.tiger.four.table.LableJTable;
import com.tiger.four.util.DataUtil;
import com.tiger.four.util.FileTUtil;

import javax.swing.*;
import javax.swing.border.AbstractBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.util.Random;
import java.util.Set;

public class InitWindow {
    public static JComboBox<String> comboBox;

    private static Timer timer;

    /**
     * 创建窗口
     */
    public static void createWindow() {
        JFrame jFrame = new JFrame("随便");
        //jFrame.setIconImage(new ImageIcon("src/main/resources/static/imgs/yu.png").getImage());
        jFrame.setIconImage(FileTUtil.readImage("static/imgs/yu.png").getImage());
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口宽高
        jFrame.setSize(600, 500);
        // 添加菜单
        jFrame.setJMenuBar(createMenus(jFrame));
        // 添加开始按钮
        JPanel jPanel = new JPanel();
        jPanel.setBackground(Color.black);

        // 添加标签
        jFrame.add(createLable(jFrame,jPanel), BorderLayout.NORTH);

        startFace(jFrame, jPanel);

        jFrame.add(jPanel, BorderLayout.CENTER);

        jFrame.setLocationRelativeTo(null);
        jFrame.setVisible(true);
    }

    /**
     * 开始界面
     * @param jFrame
     * @param jPanel
     */
    private static void startFace(JFrame jFrame, JPanel jPanel) {
        // 清空当前容器所有组件
        jPanel.removeAll();
        if(timer!=null&&timer.isRunning()){
            timer.stop();
        }
        // 加载开始图片
        JLabel imageLabel = new JLabel();
        ImageIcon secondImage = FileTUtil.readImage("static/imgs/startb1.png");//new ImageIcon("src/main/resources/static/imgs/startb1.png");
        imageLabel.setIcon(secondImage);
        jPanel.add(imageLabel);
//        增加图片点击时间
        imageLabel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                // 跳转抽签页面
                drawLotsFace(jFrame, jPanel);
            }
        });
        jFrame.revalidate();
        jFrame.repaint();
    }

    /**
     * 抽签界面
     */
    private static void drawLotsFace(JFrame jFrame, JPanel jPanel) {
        // 清空当前容器所有组件
        jPanel.removeAll();
        // 加载抽签界面
        JLabel imageLabel = new JLabel();
        ImageIcon secondImage = FileTUtil.readImage("static/imgs/move/chou2.gif"); //new ImageIcon("src/main/resources/static/imgs/move/chou2.gif");
        imageLabel.setIcon(secondImage);
        jPanel.add(imageLabel);
        // 重新渲染窗口
        jFrame.revalidate();
        jFrame.repaint();

        // 创建一个Timer,在2秒后触发ActionEvent
        timer = new Timer(2000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                // 跳转结束画面
                endFace(jFrame, jPanel, (Timer) evt.getSource());
            }
        });

        timer.start();
    }


    /**
     * 结束界面
     * @param jFrame
     * @param jPanel
     * @param timer1
     */
    private static void endFace(JFrame jFrame, JPanel jPanel, Timer timer1) {
        // 清空容器中所有组件
        jPanel.removeAll();
        // 绘制结束画面
        // 首级容器 让文字和按钮换行展示
        JPanel firstPanel = new JPanel();
        firstPanel.setLayout(new BoxLayout(firstPanel, BoxLayout.Y_AXIS)); // 设置垂直布局

        // 获取当前选择的下来框
        String selectedItem = comboBox.getSelectedItem() + "";
        Set<String> strings = DataUtil.getGoods().get(selectedItem);
        // 生成随机数,随机生成结果
        Object[] array = strings.toArray();
        int i = new Random().nextInt(array.length);
        String good = array[i] + "";

        // 二级容器(为了让文字 和 按钮都居中展示,不加会错位)
        JPanel secondPanel = new JPanel();

        // 创建物品标签
        JLabel newLabel = new JLabel(good);
        newLabel.setFont(new Font("Serif", Font.BOLD, 24));
        newLabel.setForeground(Color.GREEN);
        secondPanel.add(newLabel);


        // 创建菜单容器,保证两个菜单在一行上,且有一些间距
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
        JButton again = new JButton("再来一次");
        JButton ok = new JButton("就这个");
        //        按钮点击事件
        again.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 当按钮被点击时执行的代码
                drawLotsFace(jFrame, jPanel); // 确保这个方法在你的类中已经定义
            }
        });

        ok.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 当按钮被点击时执行的代码
                startFace(jFrame, jPanel); // 确保这个方法在你的类中已经定义
            }
        });


        // 为了在垂直布局中保持组件之间的间距,可以使用 Box.createVerticalStrut() 或添加一些空白的组件
        buttonPanel.add(again);
        buttonPanel.add(Box.createHorizontalStrut(10)); // 可选:添加水平间距
        buttonPanel.add(ok);

        firstPanel.add(secondPanel);
        firstPanel.add(Box.createVerticalStrut(249)); // 可选:添加垂直间距
        firstPanel.add(buttonPanel);


        jPanel.add(firstPanel);

        // 重新渲染
        jFrame.revalidate();
        jFrame.repaint();
        timer.stop();
    }

    /**
     * 创建菜单
     * 1.编辑物品
     * 2.编辑物品标签
     *
     * @return
     */
    public static JMenuBar createMenus(JFrame jFrame) {
        JMenuBar jMenuBar = new JMenuBar();
        JMenu setting = new JMenu("设置");
        JMenuItem editGoods = new JMenuItem("编辑物品");
//        点击菜单时触发
        editGoods.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
//                绘制编辑标签界面
                LableJTable.showLableJDialog(jFrame);
            }
        });


        JMenuItem editGoodsType = new JMenuItem("编辑物品标签");
        editGoodsType.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 编辑物品界面
                GoodsJTable.showLableJDialog(jFrame);
            }
        });
        setting.add(editGoods);
        setting.add(editGoodsType);
        jMenuBar.add(setting);
        return jMenuBar;
    }


    /**
     * 创建主页标签下拉框
     * 1.读取标签信息
     * 2.加载标签页面
     * @return
     */
    public static JPanel createLable(JFrame jFrame, JPanel jPanel) {
        JPanel nPanel = new JPanel(new GridBagLayout());

        // 创建一个字符串数组作为下拉框的选项
        Object[][] tableData = DataUtil.getLable();
        String[] options = new String[tableData.length];
        for (int i = 0; i < tableData.length; i++) {
            options[i] = tableData[i][0] + "";
        }
        JPanel son = new JPanel();
        JLabel label = new JLabel("标签:");

        // 创建一个 JComboBox 并添加选项
        InitWindow.comboBox = new JComboBox<>(options);
        InitWindow.comboBox.setSize(1, comboBox.getHeight());

        // 创建个首页按钮
        JPanel pagePanel = new JPanel();
        JButton page = new JButton("首页");
        /*page.setContentAreaFilled(false); // 去除按钮的默认填充,以便边框更明显
        page.setBorderPainted(true); // 确保边框被绘制
        page.setBorder(new RoundedBorder(10)); // 应用圆角边框*/


        page.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                startFace( jFrame,  jPanel);
            }
        });
        pagePanel.add(page);

        son.add(label);
        son.add(comboBox);
        // 设置GridBag约束
        GridBagConstraints gbc = new GridBagConstraints();
        //
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 0.1; // 左侧占90%
        gbc.anchor = GridBagConstraints.LINE_START; // 右对齐
        gbc.fill = GridBagConstraints.HORIZONTAL;
        nPanel.add(pagePanel, gbc);


        // 添加空面板占位左侧90%宽度
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.weightx = 0.8; // 左侧占90%
        gbc.fill = GridBagConstraints.BOTH;
        nPanel.add(new JPanel(), gbc);

        // 设置单选框约束
        gbc.gridx = 2;      // 第二列
        gbc.gridy = 0;
        gbc.weightx = 0.1; // 右侧占10%
        gbc.anchor = GridBagConstraints.LINE_END; // 右对齐
        gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充
        nPanel.add(son, gbc);
        return nPanel;
    }

    static class RoundedBorder extends AbstractBorder {
        private int radius;

        public RoundedBorder(int radius) {
            this.radius = radius;
        }

        @Override
        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setStroke(new BasicStroke(2));
            g2d.setPaint(Color.BLACK);

            // 绘制圆角矩形边框
            g2d.draw(new RoundRectangle2D.Double(x, y, width - 1, height - 1, radius, radius));

            g2d.dispose();
        }

        @Override
        public Insets getBorderInsets(Component c) {
            return new Insets(radius, radius, radius, radius);
        }

        @Override
        public boolean isBorderOpaque() {
            return false;
        }
    }
}

编辑页面代码-标签编辑页

java 复制代码
package com.tiger.four.table;

import com.tiger.four.InitWindow;
import com.tiger.four.util.DataUtil;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 标签列表
 */
public class LableJTable {
    public static void showLableJDialog(JFrame jFrame) {
        JDialog dialog = new JDialog(jFrame, "物品列表", true);
        dialog.setSize(500, 300);
        dialog.setLayout(new BorderLayout());
        dialog.setLocationRelativeTo(null);
        JPanel jPanel = new JPanel(new BorderLayout());
        JButton add = new JButton("新增");
        // 表格
        Object[] columnTitle = {"标签", "操作"};
        DefaultTableModel model = new DefaultTableModel(DataUtil.getLable(), columnTitle);

        JTable jTable = new JTable(model);
        // 新增点击时触发
        add.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.addRow(new Object[]{""});
                dialog.pack();
            }
        });

        // 添加 TableModelListener
        model.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                String[][] tableData = DataUtil.getLable();
                int firstRow = e.getFirstRow();
                int column = e.getColumn();
                TableModel source = (TableModel) e.getSource();

                if (column < 0) return;

                Object valueAt = source.getValueAt(firstRow, column);
                if (("删除").equals(valueAt)) return;

                // 同步首页标签下拉框
                int itemCount = InitWindow.comboBox.getItemCount();
                if (itemCount - 1 < firstRow) {
                    InitWindow.comboBox.addItem(valueAt + "");
                } else {
                    Object o = tableData[firstRow][column];
                    InitWindow.comboBox.removeItem(o + "");
                    InitWindow.comboBox.insertItemAt(valueAt + "", firstRow);
                }
                DataUtil.changeLable(firstRow, column, valueAt + "");
            }
        });

        jTable.setRowHeight(35);
        jTable.getColumnModel().getColumn(1).setCellEditor(new DeleteLableEditor(jTable));

        jTable.getColumnModel().getColumn(1).setCellRenderer(new DelectButtonRender());
        jPanel.add(add, BorderLayout.NORTH);
        jPanel.add(new JScrollPane(jTable), BorderLayout.CENTER);

        dialog.add(jPanel);
        dialog.pack();
        dialog.setVisible(true);


    }

}

/**
 * 将指定列渲染成按钮
 * 举一反三
 * 也可以时图片 单选框 多选框 等非文字组件
 *
 */

class DelectButtonRender implements TableCellRenderer {
    private JPanel panel;

    private JButton button;

    public DelectButtonRender() {
        this.initButton();

        this.initPanel();

        // 添加按钮。
        this.panel.add(this.button);
    }

    private void initButton() {
        this.button = new JButton();
        // 设置按钮的大小及位置。
        this.button.setBounds(5, 5, 80, 25);
    }

    private void initPanel() {
        this.panel = new JPanel();
        this.panel.setLayout(null);
    }


    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        this.button.setText("删除");
        return this.panel;
    }
}

/**
 * 指定列中 的单元格触发修改时调用
 * 触发修改时 可以将其位置的文字或组件 改成其他下拉框,按钮等
 */
class DeleteLableEditor extends DefaultCellEditor {
    private static final long serialVersionUID = -6546334664166791132L;

    private JPanel deletePanel;

    private JButton deleteButton;
    private JTable lableTable;

    public DeleteLableEditor(JTable table) {
        super(new JTextField());
        lableTable = table;
        this.setClickCountToStart(1);
        this.init();
        this.deletePanel.add(this.deleteButton);
    }

    /**
     * 初始化按钮
     * 当列表开始渲染时触发
     */
    private void init() {
        this.deletePanel = new JPanel();
        this.deletePanel.setLayout(null);

        this.deleteButton = new JButton();
        this.deleteButton.setBounds(0, 0, 80, 25);
        this.deleteButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JButton btn = (JButton) (e.getSource());
                ChangeEvent ce = new ChangeEvent(btn);
                lableTable.editingStopped(ce);

                int selectedRow = lableTable.getSelectedRow();
                DefaultTableModel model = (DefaultTableModel) (lableTable.getModel());
                String value = model.getValueAt(selectedRow, 0) + "";
                model.removeRow(selectedRow);
                InitWindow.comboBox.removeItem(value);
                DataUtil.deleteLable(selectedRow, 0, value);
            }
        });
    }




    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        this.deleteButton.setText("删除");
        return this.deletePanel;
    }

    @Override
    public Object getCellEditorValue() {
        return this.deleteButton.getText();
    }

}

编辑页面代码-物品编辑页

java 复制代码
package com.tiger.four.table;

import com.tiger.four.util.DataUtil;

import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 物品列表
 */
public class GoodsJTable {

    public static void showLableJDialog(JFrame jFrame) {
        JDialog dialog = new JDialog(jFrame, "物品列表", true);
        dialog.setSize(500, 300);
        dialog.setLayout(new BorderLayout());
        dialog.setLocationRelativeTo(null);
        JPanel jPanel = new JPanel(new BorderLayout());
        JButton add = new JButton("新增");

        // 表格
        Object[] columnTitle = {"物品", "标签", "操作"};
        DefaultTableModel model = new DefaultTableModel(DataUtil.getGoodsArray(), columnTitle);
        JTable jTable = new JTable(model);
        add.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                model.addRow(new Object[]{""});
                dialog.pack();
            }
        });

        // 添加 TableModelListener
        model.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                int firstRow = e.getFirstRow();
                int column = e.getColumn();
                if (column < 0) return;
                TableModel source = (TableModel) e.getSource();
                Object valueAt = source.getValueAt(firstRow, column);
                if (("删除").equals(valueAt)) {
                    return;
                }
                DataUtil.changeGoods(firstRow, column, valueAt + "");
            }
        });

        jTable.setRowHeight(35);
        jTable.getColumnModel().getColumn(2).setCellEditor(new DeleteEditor(jTable));
        jTable.getColumnModel().getColumn(1).setCellEditor(new CheckBoxEditor(jTable));
        jTable.getColumnModel().getColumn(2).setCellRenderer(new DeleteRenderer());
        jPanel.add(add, BorderLayout.NORTH);
        jPanel.add(new JScrollPane(jTable), BorderLayout.CENTER);
        dialog.add(jPanel);
        dialog.pack();
        dialog.setVisible(true);
    }
}

/**
 * 单元格渲染时 会按照指定格式渲染
 */
class DeleteRenderer implements TableCellRenderer {
    private JPanel panel;

    private JButton button;

    public DeleteRenderer() {
        this.initButton();
        this.initPanel();
        this.panel.add(this.button);
    }

    private void initButton() {
        this.button = new JButton();
        // 设置按钮的大小及位置。
        this.button.setBounds(5, 5, 80, 25);
    }

    private void initPanel() {
        this.panel = new JPanel();
        // panel使用绝对定位,这样button就不会充满整个单元格。
        this.panel.setLayout(null);
    }


    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        this.button.setText("删除");
        return this.panel;
    }
}

/**
 * 单元格触发编辑时 会按照指定格式渲染
 */
class DeleteEditor extends DefaultCellEditor {
    private static final long serialVersionUID = -6546334664166791132L;
    private JPanel buttonPanel;
    private JButton deleteButton;
    private JTable goodsTable;

    public DeleteEditor(JTable table) {
        super(new JTextField());
        goodsTable = table;
        this.setClickCountToStart(1);
        this.init();
    }

    private void init() {
        this.buttonPanel = new JPanel();
        this.buttonPanel.setLayout(null);

        this.deleteButton = new JButton();
        this.deleteButton.setBounds(0, 0, 80, 25);
        this.deleteButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (goodsTable.getCellEditor() != null) {
                    goodsTable.getCellEditor().stopCellEditing();
                }
                /*JButton btn = (JButton) (e.getSource());
                ChangeEvent ce = new ChangeEvent(btn);
                goodsTable.editingStopped(ce);*/
                int selectedRow = goodsTable.getSelectedRow();
                DefaultTableModel model = (DefaultTableModel) (goodsTable.getModel());
                String valueAt = model.getValueAt(selectedRow, 0) + "";
                model.removeRow(selectedRow);
                DataUtil.deleteGoods(selectedRow, 0, valueAt);
            }
        });
        this.buttonPanel.add(this.deleteButton);
    }



    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        this.deleteButton.setText("删除");
        return this.buttonPanel;
    }

    @Override
    public Object getCellEditorValue() {
        return this.deleteButton.getText();
    }

}

/**
 * 单元格触发编辑时 会按照指定格式渲染
 */
class CheckBoxEditor extends DefaultCellEditor {
    private JPanel boxPanel;

    private JComboBox<String> comboBox;

    private JTable goodsTable;

    public CheckBoxEditor(JTable table) {
        super(new JTextField());
        goodsTable = table;
        this.setClickCountToStart(2);
        this.init();
    }

    private void init() {
        this.boxPanel = new JPanel();
        this.boxPanel.setLayout(null);

        String[][] tableData = DataUtil.getLable();
        int goodsLength = tableData.length;
        String[] option = new String[goodsLength];
        for (int i = 0; i < goodsLength; i++) {
            option[i] = tableData[i][0] + "";
        }
        this.comboBox = new JComboBox<String>(option);
        this.comboBox.setBounds(0, 0, 80, 25);
        this.boxPanel.add(this.comboBox);
    }


    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        this.comboBox.setSelectedItem(value);
        return this.boxPanel;
    }

    @Override
    public Object getCellEditorValue() {
        return this.comboBox.getSelectedItem();
    }

}

异常

无法读取静态文件

开发阶段一切正常,打成jar 以后静态文件无法读取

原因

常规方法无法从jar(压缩包) 文件中读取文件

代码
java 复制代码
// 用此方法读取
InputStream inputStream = FileTUtil.class.getClassLoader().getResourceAsStream(path);
 

ImageIcon 加载动图 不动

原因

通过源码中观察 ,流的的方式 会导致缺少以下渲染加载动作,导致无法将动图完整加载到Icon 中,从而只显示动图的第一张图

Toolkit.getDefaultToolkit().createImage(imageData);

代码
java 复制代码
 public static ImageIcon readImage(String path){
        try (InputStream inputStream = FileTUtil.class.getClassLoader().getResourceAsStream(path)) {
            if (inputStream == null) throw new IllegalArgumentException("资源未找到: " + path);

            // 将流转换为字节数组
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            byte[] data = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, bytesRead);
            }
            byte[] imageBytes = buffer.toByteArray();
            // 此行是关键,直接new ImageIcon(inputStream);会让动图失效
            return new ImageIcon(imageBytes);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("资源加载失败", e);
        }

    }
相关推荐
SWAGGY..3 分钟前
Linux系统编程:(十三)环境变量
java·linux·算法
程序员黑豆9 分钟前
AI全栈开发 - Java:基本数据类型 vs 引用数据类型的内存存储
java·前端·ai编程
布朗克16827 分钟前
34 JVM深入理解
java·jvm
Flittly35 分钟前
【AgentScope Java新手村系列】(4)结构化输出
java·spring boot·spring·ai
wzg19690226wzg36 分钟前
rust 学习 泛型
开发语言·学习·rust
techdashen38 分钟前
Rust 基础设施团队 2025 Q4 回顾与 2026 Q1 计划
开发语言·后端·rust
红宝村村长39 分钟前
torch.autograd.Function.apply()
开发语言·python
AI科技星39 分钟前
《数术工坊:非欧射影录》类型:硬核光影·几何本源
c语言·开发语言·网络·量子计算·agi
何以解忧,唯有..1 小时前
Python 中的继承机制:从基础到高级用法详解
java·开发语言·python