Java实现进度条

仅供学习,不可转载,欢迎评论交流。

前言

在编程开发中,经常会遇到一些需要花费较长时间的操作,比如:文件上传、数据处理、任务完成的进度等等。为了提高用户的使用体验,一般情况下我们都会利用进度条的方式来实现,让用户拥有一个比较良好的使用体验。下面将介绍四种方式使用Java实现进度条的功能。

Java进度条原理

要实现进度条,我们需要知道操作的总进度和当前进度;总进度表示操作需要完成的总任务数,当前进度指已经完成的任务数。根据这两个值,我们就可以计算出当前操作进度的百分比。
总得来说进度条的基本原理就是通过在程序汇总开辟一个新的线程,将进度条和耗时任务分离开,从而达到在运行时耗时任务的过程中展示进度条的效果。

基于Java进度条的实现方式

1、使用Swing JProgressBar工具

Swing JProgessBar是Java Swing组件库中提供的一中基于进度条实现方式,使用Java Swing JProgressBar实现进度条的主要步骤如下:

package com.cpf.jdt;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
 * @author cpf
 * @DATE 2023/10/11 9:16
 */
public class JProgress {
    JFrame jf = new JFrame("测试进度条");
    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");
    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);
    public void init(){
        //组装视图
        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取indetermainate复选框是否选中
                boolean selected = indeterminate.isSelected();
                //设置当前进度条-不确定进度
                bar.setIndeterminate(selected);
                bar.setStringPainted(!selected);
            }
        });
        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取noBorder复选框是否选中
                boolean selected = noBorder.isSelected();
                bar.setBorderPainted(!selected);
            }
        });
        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);
        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);
        //把当前窗口的布局方式修改为FlowLayout
        jf.setLayout(new FlowLayout());
        jf.add(vBox);
        jf.add(bar);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);
        //通过循环模拟修改进度条的进度
        for (int i = 1; i < 101; i++){
            //修改已经完成的工作量,也就是百分比
            bar.setValue(i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        new JProgress().init();
    }
}

以上代码实现的效果如下图所示:
分为一下几种情况:
在这里插入图片描述
该显示效果是利用百分比的形式显示。
当点击不确定进度会有一个方框来回摆动,效果如下图所示:
在这里插入图片描述
点击不绘制边框时,显示效果如下图所示:
在这里插入图片描述

2、通过将耗时任务分离出来,利用子线程的方式处理

第一种方式有一个问题,就是我们展示进度时,通过一个for循环进行的,通过for循环我们模拟了一个耗时操作,很容易看出来该for循环是在主线程中进行的,也就是说耗时的操作是在主线程中进行的,如下图所示。
在这里插入图片描述
这样我们通过一个子线程来完成该耗时任务。
在这里插入图片描述
进度条在主线程中,但是耗时任务在子线程中,我们通过一个变量来记录耗时任务的进度,然后再主线程中开启一个定时器,例如200ms读取该变量值,将该变量的值显示在进度条上就可以了。

package com.cpf.jdt;

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

/**
 * @author cpf
 * @DATE 2023/10/11 9:16
 */
public class JProgress1 {
    JFrame jf = new JFrame("部署进度");
    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");
    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);
    public void init(){
        //组装视图
        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取indetermainate复选框是否选中
                boolean selected = indeterminate.isSelected();
                //设置当前进度条-不确定进度
                bar.setIndeterminate(selected);
                bar.setStringPainted(!selected);
                
            }
        });
        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取noBorder复选框是否选中
                boolean selected = noBorder.isSelected();
                bar.setBorderPainted(!selected);
            }
        });
        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);
        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);
        //把当前窗口的布局方式修改为FlowLayout
        jf.setLayout(new FlowLayout());
        jf.add(vBox);
        jf.add(bar);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);
        //开启子线程模拟耗时操作
        SimlaterActivity simlaterActivity = new SimlaterActivity(bar.getMaximum());
        //开启子线程
        new Thread(simlaterActivity).start();
        //设置定时任务
        Timer timer = new Timer(200, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //读取线程任务对象的当前完成量,设置给进度条
                int current = simlaterActivity.getCurrent();
                bar.setValue(current);
            }
        });
        //开启定时任务
        timer.start();
        //监听进度条的任务变化
        bar.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                int value = bar.getValue();
                if (value == simlaterActivity.getAmount()){
                    timer.stop();
                }
            }
        });
    }
    
    private class SimlaterActivity implements Runnable{
        //记录任务总量
        private int amount;
        public SimlaterActivity(int amount) {
            this.amount = amount;
        }
        //记录当前任务的完成量
        //由于子线程和主线程不是同一个线程,所以需要考虑变量的内存可见问题
        //volatile:内存可见,在该线程修改的值,其他线程可以立刻看到该线程修改的效果
        private volatile int current;
        public int getAmount() {
            return amount;
        }
        public void setAmount(int amount) {
            this.amount = amount;
        }
        public int getCurrent() {
            return current;
        }
        public void setCurrent(int current) {
            this.current = current;
        }
        @Override
        public void run() {
            //子线程的任务,模拟耗时操作
            while (current < amount){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                current++;
            }
        }
    }
    public static void main(String[] args){
        new JProgress1().init();
    }
}

显示效果与第一种方式是一致的
在这里插入图片描述

3、使用BoundedRangeModel的方式完成进度条功能

我们了解过Swing中很多组件的界面与数据都采用MVC的设计思想,使用MVC的设计思想可以将代码进行分块、分层:

在这里插入图片描述

上面介绍的JProgressBar也是采用的MVC的设计思想,其实JProgressBar组件内部也是通过BoundedRangeModel来完成数据的操作。
下面的代码是对之前代码的改进,通过BoundedRangeModel完成数据的设置、获取和监听。

package com.cpf.jdt;

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

/**
 * @author cpf
 * @DATE 2023/10/11 9:16
 */
public class JProgress2 {
    JFrame jf = new JFrame("部署进度");
    JCheckBox indeterminate = new JCheckBox("不确定进度");
    JCheckBox noBorder = new JCheckBox("不绘制边框");
    //创建进度条
    JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL,0,100);
    //获取进度条内置的数据模型对象
    BoundedRangeModel model = bar.getModel();
    public void init(){
        //组装视图
        //处理复选框的点击行为
        indeterminate.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取indetermainate复选框是否选中
                boolean selected = indeterminate.isSelected();
                //设置当前进度条-不确定进度
                bar.setIndeterminate(selected);
                bar.setStringPainted(!selected);
            }
        });
        
        noBorder.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获取noBorder复选框是否选中
                boolean selected = noBorder.isSelected();
                bar.setBorderPainted(!selected);
            }
        });
        Box vBox = Box.createVerticalBox();
        vBox.add(indeterminate);
        vBox.add(noBorder);
        //设置进度条的属性
        bar.setStringPainted(true);
        bar.setBorderPainted(true);
        //把当前窗口的布局方式修改为FlowLayout
        jf.setLayout(new FlowLayout());
        jf.add(vBox);
        jf.add(bar);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.pack();
        jf.setVisible(true);
        //开启子线程模拟耗时操作
        SimlaterActivity simlaterActivity = new SimlaterActivity(bar.getMaximum());
        //开启子线程
        new Thread(simlaterActivity).start();
        //设置定时任务
        Timer timer = new Timer(200, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //读取线程任务对象的当前完成量,设置给进度条
                int current = simlaterActivity.getCurrent();
//                bar.setValue(current);
                model.setValue(current);
            }
        });
        //开启定时任务
        timer.start();
        //监听进度条的任务变化
//        bar.addChangeListener(new ChangeListener() {
        model.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
//                int value = bar.getValue();
                int value = model.getValue();
                if (value == simlaterActivity.getAmount()){
                    timer.stop();
                }
            }
        });
        
    }
    
    private class SimlaterActivity implements Runnable{
        //记录任务总量
        private int amount;
        public SimlaterActivity(int amount) {
            this.amount = amount;
        }
        //记录当前任务的完成量
        //volatile:内存可见,再该线程修改的值,其他线程可以立刻看到该线程修改的效果
        private volatile int current;
        public int getAmount() {
            return amount;
        }
        public void setAmount(int amount) {
            this.amount = amount;
        }
        public int getCurrent() {
            return current;
        }
        public void setCurrent(int current) {
            this.current = current;
        }
        @Override
        public void run() {
            //子线程的任务,模拟耗时操作
            while (current < amount){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                current++;
            }
        }
    }
    
    public static void main(String[] args){
        new JProgress2().init();
    }
}

显示效果与前两种方式相同
在这里插入图片描述

4、创建进度对话框

ProgressMonitor的用法与JProgreeBar的用法基本相似,只是ProgressMonitor可以直接创建一个进度对话框。
其中,使用ProgressMonitor创建的对话框里包含的进度条非常的固定,程序甚至不能设置改进度条是否包含边框(总是包含边框),不能设置进度不确定,不能改变进度条的方向(总是水平的)。

package com.cpf.jdt;

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

/**
 * @author cpf
 * @DATE 2023/10/11 10:14
 */
public class ProgressMonitorDemo {
    Timer timer;
    public void init(){
        //创建ProgressMonitor进度对话框对象
        ProgressMonitor monitor = new ProgressMonitor(null, "等待部署完成......", "已完成:", 0, 100);
        SimlaterActivity simlaterActivity = new SimlaterActivity(100);
        new Thread(simlaterActivity).start();
        //设置定时任务
        timer = new Timer(200, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //读取当前任务量,修改进度
                int current = simlaterActivity.getCurrent();
                monitor.setProgress(current);
                //判断用户是否点击了取消按钮,点击了停止任务,关闭对话框,退出程序
                if (monitor.isCanceled()){
                    timer.stop();
                    monitor.close();
                    System.exit(0);
                }
            }
        });
        timer.start();
    }
    
    public static void main(String[] args){
        new ProgressMonitorDemo().init();
    }
    //定义一个线程任务,模拟耗时操作
    private class SimlaterActivity implements Runnable{
        //记录任务总量
        private int amount;
        public SimlaterActivity(int amount) {
            this.amount = amount;
        }
        //记录当前任务的完成量
        //volatile:内存可见,再该线程修改的值,其他线程可以立刻看到该线程修改的效果
        private volatile int current;
        public int getAmount() {
            return amount;
        }
        public void setAmount(int amount) {
            this.amount = amount;
        }
        public int getCurrent() {
            return current;
        }
        public void setCurrent(int current) {
            this.current = current;
        }
        @Override
        public void run() {
            //子线程的任务,模拟耗时操作
            while (current < amount){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                current++;
            }
        }
    }
    
}

显示效果如下图所示:
在这里插入图片描述

5、总结

Java实现进度条功能作为一种常见的实现进度条的方式,其基本原理是通过在程序中开辟一个新的线程,将进度条和耗时任务分离开,从而达到在运行耗时任务的过程中展示进度条的效果。以上提供了四种方式实现进度条的场景,针对一些有较高交互需求的场景,可以采用前后端相互配合实现进度条及时准确更新效果的方式实现更加优秀的用户交互体验。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java实现进度条的开发过程一般分为以下几个步骤: 1. 创建进度条组件:使用JProgressBar类创建进度条组件,并设置进度条的最小值、最大值和初始值; 2. 设置进度条样式:可以通过setUI()方法来设置进度条的UI样式,也可以使用L&F来设置进度条的样式; 3. 更新进度条数值:通过setValue()方法来更新进度条的数值,可以在需要更新进度条数值的地方调用该方法; 4. 监听进度条数值变化:可以通过addChangeListener()方法来添加监听器,实时监听进度条数值的变化,以便在进度条数值发生变化时做出相应的处理。 下面是一个简单的Java实现进度条的示例代码: ```java import javax.swing.*; import java.awt.*; public class ProgressBarDemo extends JFrame { private JProgressBar progressBar; // 进度条组件 public ProgressBarDemo() { setTitle("进度条示例"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(300, 100); setLocationRelativeTo(null); // 创建进度条组件,设置最小值、最大值和初始值 progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); progressBar.setValue(0); // 将进度条组件添加到窗口中 Container contentPane = getContentPane(); contentPane.add(progressBar, BorderLayout.CENTER); // 模拟进度条更新 for (int i = 0; i <= 100; i++) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } progressBar.setValue(i); } } public static void main(String[] args) { new ProgressBarDemo().setVisible(true); } } ``` 在上面的示例代码中,我们创建了一个ProgressBarDemo类,该类继承自JFrame类,实现了一个简单的进度条示例。在构造函数中,我们创建了一个进度条组件,并设置了最小值、最大值和初始值,然后将进度条组件添加到窗口中。在模拟进度条更新的过程中,我们使用了Thread.sleep()方法来模拟耗时操作,每隔100毫秒更新一次进度条的数值。最终,我们可以看到一个进度条在窗口中不断更新,直到达到100%的进度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

I心暖存人T

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

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

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

打赏作者

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

抵扣说明:

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

余额充值