Java手搓进度条实现:从简单到优雅

在Java开发中,我们经常需要处理耗时操作,并希望向用户展示进度。本文将介绍几种实现进度条的方法,从最简单的文本输出到美观的进度条显示。

一、最简单的进度显示

当我们刚开始学习编程时,可能会想到最简单的方式:在循环中输出当前进度。

public class Loop {
    public static void main(String[] args) {
        int size = 500;
        System.out.println("开始运行");
        
        for (int i = 0; i < size; i++) {
            doSth();
            System.out.print("进度:" + (i + 1) + "/" + size + "\r");
        }
        System.out.println();
        System.out.println("结束运行");
    }

    private static void doSth() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这种方式的特点:

  • 使用\r回车符将光标移到行首,实现覆盖输出
  • 简单直观,容易理解
  • 显示效果不够美观,只是简单的文本

二、使用进度条类实现更美观的显示

为了获得更美观的进度显示,我们可以创建一个专门的进度条类。下面是一个简单的进度条实现:

public class ProgressBar {
    private final int total;
    private final int width;
    private int current;
    private Style style;

    private final static Style STYLE_1 = new Style("#", " ", "");
    private final static Style STYLE_2 = new Style("#", "=", "");
    private final static Style STYLE_3 = new Style("=", " ", ">");

    public ProgressBar(int total, int width, int style) {
        this.total = total;
        this.width = width;
        this.current = 0;
        if (style == 1) {
            this.style = STYLE_1;
        } else if (style == 2) {
            this.style = STYLE_2;
        } else {
            this.style = STYLE_3;
        }
    }

    public void start() {
        System.out.println("\n开始执行任务...");
    }

    public void finish() {
        System.out.println("\n任务执行结束...");
    }

    public void update(int progress) {
        this.current = progress;
        print();
    }

    private void print() {
        double percentage = (double) current / total;
        int progressMarks = (int) (percentage * width);

        StringBuilder bar = new StringBuilder();
        bar.append(String.format("当前进度: %3d%% [", (int) (percentage * 100)));
        boolean firstRight = true;
        for (int i = 0; i < width; i++) {
            if (i < progressMarks) {
                bar.append(style.leftStr);
            } else {
                if (firstRight) {
                    bar.append(style.leftEndStr);
                    firstRight = false;
                }
                bar.append(style.rightStr);
            }
        }
        bar.append("]");

        System.out.print("\r" + bar);
        if (percentage >= 1) {
            finish();
        }
    }

    @Getter
    private static class Style {
        private String leftStr;
        private String rightStr;
        private String leftEndStr;

        public Style(String leftStr, String rightStr, String leftEndStr) {
            this.leftStr = leftStr;
            this.rightStr = rightStr;
            this.leftEndStr = leftEndStr;
        }
    }
}

这个进度条类的特点:

  • 支持自定义进度条宽度
  • 提供多种样式选择
  • 显示百分比和进度条图形
  • 自动处理开始和结束提示

三、使用进度条类展示进度

有了进度条类,我们可以这样使用它:

public class LoopProgress {
    public static void main(String[] args) {
        int size = 500;
        ProgressBar progressBar = new ProgressBar(size, 60, 2);

        System.out.println("开始运行");
        for (int i = 0; i < size; i++) {
            doSth();
            progressBar.update(i + 1);
        }
    }

    private static void doSth() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这种方式的特点:

  • 代码更加模块化,进度条逻辑与业务逻辑分离
  • 显示效果更加美观,有图形化的进度条
  • 可以轻松扩展更多功能

四、进度条实现的深入解析

1. 进度条的核心原理

进度条的核心原理是利用控制台的回车符\r,将光标移到行首,然后覆盖输出新的进度信息。这样就能在同一行更新进度,而不是不断打印新行。

2. 进度条样式的设计

ProgressBar类中,我们使用了内部类Style来定义不同的进度条样式:

private static class Style {
    private String leftStr;    // 已完成部分的字符
    private String rightStr;   // 未完成部分的字符
    private String leftEndStr; // 进度条末尾的字符
}

通过组合这些字符,我们可以创建不同风格的进度条:

  • 样式1: [#### ]
  • 样式2: [####====]
  • 样式3: [====> ]

3. 进度计算与显示

进度条的核心计算逻辑在print()方法中:

private void print() {
    double percentage = (double) current / total;
    int progressMarks = (int) (percentage * width);

    StringBuilder bar = new StringBuilder();
    bar.append(String.format("当前进度: %3d%% [", (int) (percentage * 100)));
    boolean firstRight = true;
    for (int i = 0; i < width; i++) {
        if (i < progressMarks) {
            bar.append(style.leftStr);
        } else {
            if (firstRight) {
                bar.append(style.leftEndStr);
                firstRight = false;
            }
            bar.append(style.rightStr);
        }
    }
    bar.append("]");

    System.out.print("\r" + bar);
    if (percentage >= 1) {
        finish();
    }
}

这段代码:

  1. 计算当前进度的百分比
  2. 根据百分比和进度条宽度计算已完成的标记数量
  3. 构建进度条字符串,包括百分比和图形化进度条
  4. 使用\r回车符覆盖输出进度条
  5. 当进度达到100%时,调用finish()方法

五、进度条的扩展与优化

基于上述实现,我们可以进一步扩展和优化进度条:

  1. 添加颜色支持:使用ANSI转义序列为进度条添加颜色
  2. 添加速度显示:显示每秒处理的项目数
  3. 添加预计剩余时间:根据当前速度计算预计完成时间
  4. 支持多线程:在多线程环境中安全地更新进度
  5. 支持进度条嵌套:显示子任务的进度

六、总结

通过本文的介绍,我们了解了从简单的文本进度显示到美观的进度条实现的演进过程。一个好的进度条不仅能够提供直观的进度反馈,还能提升用户体验。

在实际开发中,我们可以根据项目需求选择合适的进度条实现,或者基于本文的代码进行定制化开发,以满足特定的显示需求。

希望本文对您有所帮助,欢迎在评论区分享您的想法和经验!