Как отобразить индикатор выполнения на стандартной консоли с помощью Java
Как на стандартной консоли отображается индикатор выполнения, особенно в случаях, когда для загрузки пакетов используются менеджеры вроде npm? Рассмотрим решение.
Сначала создадим метод с именем printMsgWithProgressBar(), который содержит следующие параметры:
String— выводимое сообщение;int— длина индикатора выполнения;long— интервал времени, ответственный за скорость показа анимации.
Поскольку мы будем вызывать метод из static main(), необходимо добавить модификатор static и к методу printMsgWithProgressBar(). В итоге, метод будет выглядеть так: public static void printMsgWithProgressBar(String message, int length, long timeInterval){ }.
Воспользуемся Java-классом StringBuilder для построения индикатора выполнения в качестве строки String. Возможно, у вас возникнет вопрос, каким образом прогресс-бар можно сформировать из строки. Но теперь мы прибегнем к магии, задействовав 2 символа Unicode.
- Символ U+2591 ░, чтобы показать незавершенный процесс выполнения.
- Символ U+2588 █ — для завершенного процесса.
Обратите внимание: эти символы могут не поддерживаться в консолях, если те не поддерживают символы Unicode в целом. В таком случае для отображения индикатора выполнения можно использовать другие символы, такие как = и — .
Для создания прогресс-бара возьмем StringBuilder. Следует использовать фабричный метод generate() из Java Stream API для создания индикатора выполнения с Unicode-символом незавершенности. Для этой цели можно также применить простой цикл. На этом этапе код должен выглядеть так:
public static void printMsgWithProgressBar(String message, int length, long timeInterval){
char incomplete = '░'; // U+2591 Unicode Character
char complete = '█'; // U+2588 Unicode Character
StringBuilder builder = new StringBuilder();Stream.generate(() -> incomplete).limit(length).forEach(builder::append);}Прежде чем показывать индикатор выполнения, нужно задать выводимое сообщение. Объявим цикл for с итератором int от 0 до length-1. Внутри цикла заменим i-тый символ builder на полный блок символов Unicode и выведем на печать индикатор выполнения. Теперь код должен выглядеть следующим образом:
public static void printMsgWithProgressBar(String message, int length, long timeInterval)
{
char incomplete = '░'; // U+2591 Unicode Character
char complete = '█'; // U+2588 Unicode Character
StringBuilder builder = new StringBuilder();Stream.generate(() -> incomplete).limit(length).forEach(builder::append);for(int i = 0; i < length; i++) { builder.replace(i,i+1,String.valueOf(complete));System.out.print(builder);}}Если выполнять этот метод из main(), он не будет выводить индикатор. Вместо этого String будет выводиться в каждом цикле заново, иногда даже с переносом на следующую строку. Как же выводить обновленный индикатор выполнения в одной и той же строке на каждой итерации?
Мы будем использовать возврат каретки \r, который заставляет консоль перемещать курсор в начало строки после того, как завершится печать. Перед выводом на печать нам необходимо добавить символ \r в builder. Теперь код выглядит так:
public static void printMsgWithProgressBar(String message, int length, long timeInterval)
{
char incomplete = '░'; // U+2591 Unicode Character
char complete = '█'; // U+2588 Unicode Character
StringBuilder builder = new StringBuilder();Stream.generate(() -> incomplete).limit(length).forEach(builder::append);for(int i = 0; i < length; i++) { builder.replace(i,i+1,String.valueOf(complete));String progressBar = "\r" + builder;System.out.print(progressBar);}}Теперь при запуске кода мы получаем индикатор выполнения задачи на одной строке. Пока он не выглядит как анимация, так как заполняется без задержек.
Чтобы добавить эту задержку, воспользуемся величиной timeInterval в качестве аргумента метода. Выполнение кода будет останавливаться через регулярные промежутки времени с помощью метода Thread.sleep() с временным интервалом timeInterval в качестве параметра.
Вот и все! Теперь при запуске кода вы получите анимированный индикатор выполнения на консоли. Вот весь код целиком:
import java.util.stream.Stream;public class Main{
public static void printMsgWithProgressBar(String message, int length, long timeInterval) {
char incomplete = '░'; // U+2591 Unicode Character char complete = '█'; // U+2588 Unicode Character
StringBuilder builder = new StringBuilder(); Stream.generate(() -> incomplete).limit(length).forEach(builder::append);
System.out.println(message); for(int i = 0; i < length; i++) { builder.replace(i,i+1,String.valueOf(complete)); String progressBar = "\r"+builder; System.out.print(progressBar); try { Thread.sleep(timeInterval); } catch (InterruptedException ignored) { } } } public static void main(String[] args) { printMsgWithProgressBar("Loading", 25, 60); }}Так прогресс-бар будет выглядеть, пока он не заполнен:
А так — когда анимация завершится: