关于Swing中使用Substance皮肤和界面刷新问题一角

本文介绍Swing界面刷新的最佳实践,包括如何避免线程安全问题,使用SwingUtilities.invokeLater确保界面更新操作在事件分派线程中执行,并通过实例演示正确的线程使用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题一、Swing使用Substance皮肤教程
请点链接刚开始遇到的问题基本都有http://insubstantial.github.io/insubstantial/substance/docs/getting-started.html
温馨提示:需要的包:
皮肤包:Subtance.jar
下载的包包括这三个前缀的子包
org.pushingpixels.lafplugin…
org.pushingpixels.substance…
org/pushingpixels/lafwidget…
否则会报NoClassDefFoundError
支持包:trident.jar(6.0以上的版本需要)

问题二、Swing中org.jvnet.substance.api.UiThreadingViolationException: Component state change must be done on Event Dispatch Thread 异常或者Exception in thread “Thread-6” java.lang.IllegalArgumentException: Cannot change state of non-idle timeline异常
这是因为Swing本身的线程不安全所致,swing界面刷新线程必须在由EDT分配,可以在SwingUtilities.invokeLater(()->{refreshCode});
注意,refreshCode()中不可包含耗时的工作,否则会阻塞UI线程,无法及时更新,下面是自己的爬虫界面刷新例子:
这里写图片描述这里写图片描述
部分有关该问题的代码如下:

    class FilterProgress extends JProgressBar implements Runnable{
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private int count;
        FilterProgress(){
            this.setMinimum(0);
            this.setMaximum(100);
        }
        public void run(){
            //界面更新代码1
            SwingUtilities.invokeLater(()->{
                crButton.setEnabled(false);
                saveButton.setEnabled(false);
                flButton.setEnabled(false);
            });
            HashSet<IPInfo> list=c.getList();
            int size=list.size();
            count=1;
            float inc=100.0F/size;
            Iterator<IPInfo> it=list.iterator();

            while(it.hasNext()){
                IPInfo ipf=it.next();
                String ip=ipf.getIP();
                String port=ipf.getPort();
                StringBuilder str=new StringBuilder("");
                str.append("testing..."+line);
                str.append(ipf.toString()+line+"状态:");
                long start=System.currentTimeMillis();
                if (!IPFilter.filter(ip, port)){
                    it.remove();
                    str.append("失败");
                    text.setForeground(Color.RED);
                }else{
                    str.append("成功");
                    text.setForeground(Color.green);
                }
                long end=System.currentTimeMillis();
                str.append(line+"耗时:"+(end-start)+"ms"+line);
                //界面更新代码2
                SwingUtilities.invokeLater(()->{
                    text.append(str.toString());
                    this.setValue((int)(count++*inc));
                });
            }

            text.append("剩余IP数"+list.size());
            //界面更新代码3
            SwingUtilities.invokeLater(()->{
                crButton.setEnabled(true);
                saveButton.setEnabled(true);
                flButton.setEnabled(true);
            });
        }
    }

    class CountLabel implements Runnable {
        private int now=0,pre=-1;
        @Override
        public void run() {
            // TODO Auto-generated method stub
            // TODO Auto-generated meth9od stub
            //界面更新代码
            SwingUtilities.invokeLater(()->{
                crButton.setEnabled(false);
                saveButton.setEnabled(false);
                flButton.setEnabled(false);
            });
            String result_start = "共抓取";
                String result_end = "条结果";
            now=0;
            pre=-1;
            while (!c.getState()) {
                now=c.getList().size();
                String str=result_start+now+result_end;
                //界面更新代码
                if (now!=pre){
                    SwingUtilities.invokeLater(()->{
                        countLabel.setText(str);
                    });
                }
                pre=now;
            }
            SwingUtilities.invokeLater(()->{
                    crButton.setEnabled(true);
                    saveButton.setEnabled(true);
                    flButton.setEnabled(true);
            });
        }   
    }
    @Override
    //按钮监听器中的事件
    public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub
        if (arg0.getActionCommand().equals("crawler")){
            //因为要动态刷新界面所以要另开线程
            new Thread(new CountLabel()).start();
            //开启爬取任务的线程
            new Thread(c).start();
        }else if (arg0.getActionCommand().equals("filter")){
            if (c.getState())
                //进度条的线程
                new Thread(progress).start();
            else
                JOptionPane.showMessageDialog(null, "爬取未完成");
        }else if(arg0.getActionCommand().equals("save")){
            if (c.getState()){
                c.saveUsefulIp();
            }
            else
                JOptionPane.showMessageDialog(null, "爬取未完成");
        }
    }

姑且分析上述代码,也许有人会直接在actionPerformed中这样写:

public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub
        if (arg0.getActionCommand().equals("crawler")){
            //因为要动态刷新界面所以要另开线程
            //修改部分****************************
            SwingUtilities.invokeLater(new CountLabel());
            //开启爬取任务的线程
            new Thread(c).start();
        }else if (arg0.getActionCommand().equals("filter")){
            if (c.getState())
                //进度条的线程
                new Thread(progress).start();
            else
                JOptionPane.showMessageDialog(null, "爬取未完成");
        }else if(arg0.getActionCommand().equals("save")){
            if (c.getState()){
                c.saveUsefulIp();
            }
            else
                JOptionPane.showMessageDialog(null, "爬取未完成");
        }
    }

这样虽然可以去掉CountLabel中的多处调用SwingUtilities.invokeLater问题,但是却看不到界面刷新的情况,原因是CountLabel本身有个while循环,是个耗时任务,会阻塞UI主线程,无法实现上层组件刷新(因为子组件刷新是由父组件触发的)。

问题三、我也不知道答案,在界面刷新的代码里进行运算会造成阻塞

class CountLabel implements Runnable {
        private int now=0,pre=-1;
        @Override
        public void run() {
            // TODO Auto-generated method stub
            // TODO Auto-generated meth9od stub
            //界面更新代码
            SwingUtilities.invokeLater(()->{
                crButton.setEnabled(false);
                saveButton.setEnabled(false);
                flButton.setEnabled(false);
            });
            String result_start = "共抓取";
                String result_end = "条结果";
            now=0;
            pre=-1;
            while (!c.getState()) {
                now=c.getList().size();
                //String str=result_start+now+result_end;
                //界面更新代码
                if (now!=pre){
                    SwingUtilities.invokeLater(()->{
                    //setText中的字符串运算会阻塞线程造成界面无法刷新
                        countLabel.setText(
                        result_start+now+result_end);
                    });
                }
                pre=now;
            }
            SwingUtilities.invokeLater(()->{
                    crButton.setEnabled(true);
                    saveButton.setEnabled(true);
                    flButton.setEnabled(true);
            });
        }   
    }

如有错误,欢迎批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

木子的木木

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

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

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

打赏作者

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

抵扣说明:

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

余额充值