2013-01-11 67 views
2

我想添加一個JProgressBar到我的程序,但它不會更新!一旦它導致100%,價值只會改變。這是我的方法。JProgressBar不會更新

public void downloadImages(List<String> images) { 
    if (errorCode == 0) { 
     for (int i = 0; i < images.size(); i++) { 
      if (errorCode == 0) { 
       main.progressLabel.setText("Downloading image " + Integer.toString(i + 1) + " of " + Integer.toString(images.size())); 
       String imageStr = images.get(i); 
       String imageName = imageStr.substring(imageStr.lastIndexOf("/") + 1); 
       try { 
        URL url = new URL(imageStr); 
        InputStream in = url.openStream(); 
        OutputStream out = new FileOutputStream(saveDirectory + imageName); 

        byte[] b = new byte[2048]; 
        int length; 
        while ((length = in.read(b)) != -1) { 
         out.write(b, 0, length); 
        } 

        in.close(); 
        out.close(); 
       } catch (MalformedURLException e) { 
        errorCode = BAD_URL; 
       } catch (IOException e) { 
        errorCode = INVALID_PATH; 
       } 
       main.progressBar.setValue(((i+1)/images.size())*100); 
      } 

     } 
    } 
} 

更改進度欄值位於上述方法的底部。

以下是我如何調用該方法。

public void download() { 
    final Downloader downloader = new Downloader(this, albumUrl.getText(), downloadPath.getText()); 
    progressBar.setValue(0); 
    downloadButton.setEnabled(false); 

    new Thread(new Runnable() { 
     public void run() { 
      List<String> images = downloader.getImages(downloader.getPageSource()); 
      downloader.downloadImages(images); 
      if (downloader.getErrorCode() == 0) { 
       progressLabel.setText("All images have been downloaded!"); 
      } else { 
       String error = ""; 
       switch (downloader.getErrorCode()) { 
       case Downloader.BAD_URL: 
       case Downloader.NOT_IMGUR_ALBUM: 
        error = "The URL entered is either invalid or does not link to an Imgur album."; 
        break; 
       case Downloader.BLANK_URL: 
        error = "The album URL field cannot be blank."; 
        break; 
       case Downloader.INVALID_PATH: 
        error = "The system cannot find the download directory path specified."; 
        break; 
       case Downloader.BLANK_PATH: 
        error = "The download directory cannot be blank."; 
        break; 
       case Downloader.CANNOT_READ_URL: 
        error = "An error occoured while reading the URL."; 
        break; 
       case Downloader.PARSING_ERROR: 
        error = "An error occoured while parsing the URL."; 
        break; 
       } 
       JOptionPane.showMessageDialog(Main.this, error, "Error", 0); 
      } 
      downloadButton.setEnabled(true); 
     } 
    }).start(); 
} 

編輯:上面的問題根本不是問題之一,程序使用整數除法而不是小數。

回答

4

images.size()是一個整數,所以是i+1,所以發生了什麼是十進制是越來越截斷。你應該做的事情所以像

main.progressBar.setValue((int)((i+1)/(double)images.size())/100))

這是什麼要做的是,它會確保i+1是由一個十進制能夠數據類型劃分,which'll返回更具包容性的數據類型(在這種情況下爲double),然後它會用int分割一個double,這將毫無問題,因爲它會返回一個double,因爲它更具包容性。然後我們將其轉換爲int,因爲這是數據類型setValue()想要的。搖擺的

+0

+1我還在忙着輸入這個響應。現在的代碼Jonathan肯定會一直得到0。解決精度問題以及將用戶界面更新代碼從EDT移開應該使其正常工作。 –

+0

+1 yup另一個問題OP有,也許是造成實際問題的一個問題,但在長時間運行的循環中更新Swing edt/on edt是不好的 –

+0

我從來不知道java做到了這一點,對不起,但我從你們那裏學到了很多東西美國東部時間,所以謝謝:D –

7

主要的問題是,您通過在GUI EDT上執行長時間運行的任務來阻止Event Dispatch Thread

寧可使用SwingWorker

這裏是一個小例子:

enter image description here

import java.awt.BorderLayout; 
import java.awt.Cursor; 
import java.awt.Insets; 
import java.awt.Toolkit; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.beans.PropertyChangeEvent; 
import java.beans.PropertyChangeListener; 
import java.util.Random; 
import javax.swing.BorderFactory; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JProgressBar; 
import javax.swing.JScrollPane; 
import javax.swing.JTextArea; 
import javax.swing.SwingWorker; 

public class ProgressBarDemo extends JPanel { 

    private JButton startButton; 
    private JTextArea taskOutput; 

    public ProgressBarDemo() { 
     super(new BorderLayout()); 

     final JProgressBar progressBar = new JProgressBar(0, 100); 
     progressBar.setValue(0); 
     progressBar.setStringPainted(true); 

     taskOutput = new JTextArea(5, 20); 
     taskOutput.setMargin(new Insets(5, 5, 5, 5)); 
     taskOutput.setEditable(false); 

     // Create the demo's UI. 
     startButton = new JButton("Start"); 
     startButton.setActionCommand("start"); 
     startButton.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent ae) { 
       startButton.setEnabled(false); 
       setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 
       // Instances of javax.swing.SwingWorker are not reusuable, so 
       // we create new instances as needed. 
       final Task task = new Task(); 
       task.addPropertyChangeListener(new PropertyChangeListener() { 
        @Override 
        public void propertyChange(PropertyChangeEvent pce) { 
         if ("progress".equals(pce.getPropertyName())) { 
          int progress = (Integer) pce.getNewValue(); 
          progressBar.setValue(progress); 
          taskOutput.append(String.format("Completed %d%% of task.\n", task.getProgress())); 
         } 
        } 
       }); 
       task.execute(); 
      } 
     }); 

     JPanel panel = new JPanel(); 
     panel.add(startButton); 
     panel.add(progressBar); 

     add(panel, BorderLayout.PAGE_START); 
     add(new JScrollPane(taskOutput), BorderLayout.CENTER); 
     setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 

    } 

    /** 
    * Create the GUI and show it. As with all GUI code, this must run on the 
    * event-dispatching thread. 
    */ 
    private static void createAndShowGUI() { 
     JFrame frame = new JFrame("ProgressBarDemo"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     JPanel progressBarPanel = new ProgressBarDemo(); 
     frame.add(progressBarPanel); 

     frame.pack(); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     // Schedule a job for the event-dispatching thread: 
     // creating and showing this application's GUI. 
     javax.swing.SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    private class Task extends SwingWorker<Void, Void> { 

     /* 
     * Main task. Executed in background thread. 
     */ 
     @Override 
     public Void doInBackground() { 
      Random random = new Random(); 
      int progress = 0; 
      // Initialize progress property. 
      setProgress(0); 
      while (progress < 100) { 
       // Sleep for up to one second. 
       try { 
        Thread.sleep(random.nextInt(1000)-15); 
       } catch (InterruptedException ignore) { 
       } 
       // Make random progress. 
       progress += random.nextInt(10); 
       setProgress(Math.min(progress, 100)); 
      } 
      return null; 
     } 

     /* 
     * Executed in event dispatching thread 
     */ 
     @Override 
     public void done() { 
      Toolkit.getDefaultToolkit().beep(); 
      startButton.setEnabled(true); 
      setCursor(null); // turn off the wait cursor 
      taskOutput.append("Done!\n"); 
     } 
    } 
} 
+1

哇,這是一個快速更新與運行的例子;) – MadProgrammer

+0

我試過,但它仍然沒有工作!我編輯帖子以顯示我是如何稱爲downloadImages方法的 –

+0

@JonathanBeaudoin uhm我建議Swing worker,您的編輯顯示正在使用線程並且沒有SwingWorker?請閱讀我給出的鏈接...還有其他人的帖子 –

3

兩個規則...

  1. 不要阻塞事件調度線程
  2. 不更新從事件指派線程的出方的任何UI組件。

我敢肯定,還有更多,但打破這兩者或兩者,並期望發生很多不好的事情。

Swing是一個單線程API。有一個負責調度事件的線程,包括重繪請求。如果你阻塞這個線程(執行耗時的任務,I/O等等),你將停止處理來自用戶的輸入並將重繪請求分發給組件 - 因此沒有任何更新和你的應用程序會看起來像掛起...

你需要讓你的圖片加載代碼從EDT ...

看看Concurrency in Swing更多的細節,特別是看看Worker Threads and SwingWorker

+0

+1我喜歡規則:) –