HOW-TO: JProgressBar no actualiza (Java)

Publicado por Alejandro Escario en

progress bar vacía

De muchos es sabidos que cualquier aplicación un poco compleja hace recomendable el uso de una barra de progreso para indicar lo que se está haciendo en algún momento dado de la ejecución, como por ejemplo a la hora de procesar grandes cantidades de datos o simplemente al guardar grandes cantidades de datos en un archivo, …

Pues bien, supongamos que, nuestro complejo programa consiste en una barra de progreso y de un botón. Al pulsar el botón, la barra de progreso se llena del 0% al 100% en cuestión de 1 segundo en pasos de 1 en 1, de manera que cada 10ms  el porcentaje de barra rellena tiene que aumentar en 1/100.

progress bar llena

El código de este programa consistiría en algo tan simple como lo siguiente:


package jprogressbar;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;

/**
*
* @author Alejandro
*/
public class Main {

public static JProgressBar bar;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable(){

public void run() {
displayGUI();
}

});
}

private static void displayGUI() {
JFrame frame = new JFrame("JProgressBar");

Main.bar = new JProgressBar(0, 100);
frame.getContentPane().add(bar);

JButton btn = new JButton("Press me!");
btn.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {
for(int i = 0; i <= 100; i++){
bar.setValue(i);
try {
Thread.sleep(10);
} catch (InterruptedException ex) {}
}
}

});
frame.getContentPane().add(btn, BorderLayout.SOUTH);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}

Este programa, ejecutamos en el mismo hilo la interfaz y las funciones de nuestro código, lo que hace que cuando estemos dentro de la función no le dejemos a los métodos que repintan la interfaz cada X tiempo que se ejecute pero, ¿qué quiere decir esto? pues que, visualmente, el programa para nosotros tendremos dos estados:

  1. Un estado inicial en el cual la barra está vacía (antes de pulsar el botón)
  2. Un estado final en el cual la barra de estado está llena al 100%.

Pero obviamente este no es el resultado deseado, ya que queremos ver todos los puntos intermedios por lo que pasa la barra de estado. Para hacer lo mencionado anteriormente, basta con ejecutar la lógica de negocio en otro hilo, de manera que el hilo principal está libre en todo momento para repintar la barra de progreso.

progress bar medio llena

package jprogressbar;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;

/**
*
* @author Alejandro
*/
public class Main implements Runnable {

public static JProgressBar bar;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable(){

public void run() {
displayGUI();
}

});
}

private static void displayGUI() {
JFrame frame = new JFrame("JProgressBar");

Main.bar = new JProgressBar(0, 100);
frame.getContentPane().add(bar);

JButton btn = new JButton("Press me!");
btn.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {
Main m = new Main();
Thread th = new Thread(m);
th.start();
}

});
frame.getContentPane().add(btn, BorderLayout.SOUTH);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}

public void run() {
for(int i = 0; i <= 100; i++){
bar.setValue(i);
try {
Thread.sleep(10);
} catch (InterruptedException ex) {}
}
}
}

Listo, ya funciona!

essbar;import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;

/**
*
* @author Alejandro
*/
public class Main implements Runnable {

public static JProgressBar bar;

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable(){

public void run() {
displayGUI();
}

});
}

private static void displayGUI() {
JFrame frame = new JFrame(«JProgressBar»);

Main.bar = new JProgressBar(0, 100);
frame.getContentPane().add(bar);

JButton btn = new JButton(«Press me!»);
btn.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {
Main m = new Main();
Thread th = new Thread(m);
th.start();
}

});
frame.getContentPane().add(btn, BorderLayout.SOUTH);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}

public void run() {
for(int i = 0; i <= 100; i++){
bar.setValue(i);
try {
Thread.sleep(10);
} catch (InterruptedException ex) {}
}
}
}

Categorías: Programación