2010-05-25 25 views
5

"Sự cố" của tôi có thể được mô tả bằng cách sau. Giả sử chúng tôi có một quá trình chuyên sâu mà chúng tôi muốn chạy trong nền và cập nhật thanh Swing JProgress. Giải pháp rất dễ dàng:Cách ủy quyền xuất bản của SwingWorker cho các phương thức khác

import java.util.List; 

import javax.swing.JOptionPane; 
import javax.swing.JProgressBar; 
import javax.swing.SwingWorker; 


/** 
* @author Savvas Dalkitsis 
*/ 
public class Test { 

    public static void main(String[] args) { 
     final JProgressBar progressBar = new JProgressBar(0,99); 
     SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){ 

      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size()-1)); 
      } 

      @Override 
      protected Void doInBackground() throws Exception { 

       for (int i=0;i<100;i++) { 
        publish(i); 
        Thread.sleep(300); 
       } 

       return null; 
      } 

     }; 
     w.execute(); 
     JOptionPane.showOptionDialog(null, 
       new Object[] { "Process", progressBar }, "Process", 
       JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
       null, null, null); 
    } 

} 

Bây giờ giả sử rằng tôi có nhiều phương pháp mất nhiều thời gian. Ví dụ, chúng tôi có một phương pháp tải một tập tin từ một máy chủ. Hoặc cách khác tải lên máy chủ. Hoặc bất cứ điều gì thực sự. Cách thích hợp để ủy quyền phương thức xuất bản cho các phương thức đó để chúng có thể cập nhật GUI một cách thích hợp là gì?

gì tôi đã tìm thấy cho đến nay là thế này (giả định rằng phương pháp "aMethod" cư trú trong một số gói khác ví dụ):

import java.awt.event.ActionEvent; 
import java.util.List; 

import javax.swing.AbstractAction; 
import javax.swing.Action; 
import javax.swing.JOptionPane; 
import javax.swing.JProgressBar; 
import javax.swing.SwingWorker; 


/** 
* @author Savvas Dalkitsis 
*/ 
public class Test { 

    public static void main(String[] args) { 
     final JProgressBar progressBar = new JProgressBar(0,99); 
     SwingWorker<Void, Integer> w = new SwingWorker<Void, Integer>(){ 

      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size()-1)); 
      } 

      @SuppressWarnings("serial") 
      @Override 
      protected Void doInBackground() throws Exception { 

       aMethod(new AbstractAction() { 

        @Override 
        public void actionPerformed(ActionEvent e) { 
         publish((Integer)getValue("progress")); 
        } 
       }); 

       return null; 
      } 

     }; 
     w.execute(); 
     JOptionPane.showOptionDialog(null, 
       new Object[] { "Process", progressBar }, "Process", 
       JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
       null, null, null); 
    } 

    public static void aMethod (Action action) { 
     for (int i=0;i<100;i++) { 
      action.putValue("progress", i); 
      action.actionPerformed(null); 
      try { 
       Thread.sleep(300); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

} 

Nó hoạt động nhưng tôi biết nó thiếu cái gì đó. Có suy nghĩ gì không?

Trả lời

0

Có thể làm một SwingWorker cho mỗi phương pháp dài. Mỗi SwingWorker có mức độ tiến bộ của riêng nó.

Mỗi SwingWorker cập nhật cấp độ tiến trình của riêng nó trong phương thức doInBackground, sau đó gọi xuất bản. Bên trong phương thức xử lý, vì vậy bên trong EDT, mỗi SwingWorker đọc nó là cấp độ tiến bộ, và cập nhật mô hình và thanh tiến trình nhìn chung.

+0

nếu chúng ta chỉ có một phương pháp dài khủng khiếp thì sao? Tôi không downvoting, nhưng đây không phải là một giải pháp – Xorty

+0

Bạn có thể downvoting nếu câu trả lời của tôi là xấu. Nhưng tôi đọc trong câu hỏi "Ví dụ chúng tôi có một phương pháp tải một tập tin từ một máy chủ. Hoặc một cái khác tải lên máy chủ. Hoặc bất cứ điều gì thực sự." ... vì vậy tôi cho rằng có rất nhiều phương pháp, không chỉ là một Dài ? – Istao

+0

Vấn đề tôi có là những phương pháp này có thể được gọi từ các phần khác nhau của mã. Và yếu tố gui họ sẽ cần phải làm mới sẽ không phải lúc nào cũng giống nhau. Với giải pháp của tôi tôi có thể tạo ra một phương pháp chung và mỗi lần tôi tạo ra một swingworker mới tôi có thể assing phương pháp xử lý trên gui của tôi. –

0

Tôi gặp sự cố tương tự. Dưới đây là những gì tôi tìm thấy, có thể câu trả lời thực sự chính xác thiếu, nhưng hãy thử:

  • nếu chúng tôi có nhiều lần lặp lại, chúng tôi có thể cập nhật thanh tiến trình theo phương pháp doInBackGround(). JProgressBar là tham số hàm tạo của SwingWorker của chúng ta, mở rộng SwingWorker (vì vậy có, chúng ta sử dụng tùy chỉnh)
  • nếu chúng ta không lặp lại và không thể ngắt phương thức mất nhiều thời gian để hoàn thành. nó như hầu hết mọi người (vì vậy thanh tiến trình của chúng tôi không có quá trình tuyến tính nhưng nó chỉ làm mới giá trị sau khi một phần công việc được thực hiện). Tin xấu là, nếu phương pháp của chúng tôi là thanh công việc duy nhất (e.e. gửi e-mail trên nền) sẽ trở thành đột nhiên đầy đủ. Không phải là rất tốt đẹp mặc dù, cho phép hãy xem tùy chọn thứ ba
  • điều này có thể khá điên và hiệu suất làm chậm, đó là tất cả vì ứng dụng của chúng tôi phải là ưa thích. Vì vậy, chúng ta hãy lấy mã nguồn của phương thức mất nhiều thời gian để hoàn thành. Chúng tôi ghi đè lên và dán chính xác cùng một mã, nhưng chúng tôi thêm một tham số nữa - hãy đoán xem, có JProgressBar. Bên trong phương thức chúng ta tạo Thread sẽ chạy cho đến khi một số tham số boolean (phương thức chỉ ra cờ cuối cùng được hoàn thành) được đặt đúng. Chủ đề sẽ tiếp tục cập nhật JProgressBar trong một số khoảng thời gian hợp lý. Vấn đề lớn nhất là giả định, khoảng thời gian hợp lý là bao nhiêu. Chúng tôi nên thực hiện một số thử nghiệm và ước tính giá trị trong khoảng thời gian.

Ở điểm thứ ba, tôi đã mô tả cách thực thi Thread từ phương thức hoàn thành một số nhiệm vụ không lặp lại (ít nhất là không có trong mã Java của chúng tôi) và không thể bị gián đoạn. Bản cập nhật chủ đề JProgressBar được đưa ra dưới dạng tham số phương thức. Đây là, tuy nhiên, definitelly chậm như tinh khiết phương pháp gọi

10

(Tôi đang cập nhật câu trả lời của tôi để làm cho nó rõ ràng hơn và tổng quát)

Mặc dù bạn đã tách thành công logic và trình bày của bạn, nó không được thực hiện trong một cách cho vay để tái sử dụng mã.Java của PropertyChangeSupport làm cho nó dễ dàng để decouple logic từ trình bày bằng cách thực hiện bound properties, và nhận được một số tái sử dụng đáng kể. Ý tưởng là sử dụng các trình xử lý sự kiện thay vì các đối tượng hành động.

Đầu tiên, khái niệm hóa sự trừu tượng hóa. Các công việc nền cần phải "hét lên" (xuất bản) với GUI không liên tục, và GUI cần phải lắng nghe nó. Hai lớp học chung sẽ mã hóa ý tưởng này:

/** 
* Wrapper for the background logic. 
* 
* <T> return type 
* <S> intermediary type (the "shout out") 
*/ 
public static abstract class LoudCall<T, S> implements Callable<T> { 

    private PropertyChangeSupport pcs; 
    private S shout; 

    public LoudCall() { 
     pcs = new PropertyChangeSupport(this); 
    } 

    public void shoutOut(S s) { 
     pcs.firePropertyChange("shoutOut", this.shout, 
       this.shout = s); 
    } 

    public void addListener(PropertyChangeListener listener) { 
     pcs.addPropertyChangeListener(listener); 
    } 

    public void removeListener(PropertyChangeListener listener) { 
     pcs.removePropertyChangeListener(listener); 
    } 

    @Override 
    public abstract T call() throws Exception; 
} 

/** 
* Wrapper for the GUI listener. 
* 
* <T> return type 
* <S> intermediary type (the "shout out" to listen for) 
*/ 
public static abstract class ListenerTask<T, S> extends SwingWorker<T, S> 
     implements PropertyChangeListener { 

    private LoudCall<T, S> aMethod; 

    public ListenerTask(LoudCall<T, S> aMethod) { 
     this.aMethod = aMethod; 
    } 

    @Override 
    protected T doInBackground() throws Exception { 
     aMethod.addListener(this); 
     return aMethod.call(); 
    } 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     if ("shoutOut".equals(evt.getPropertyName())) { 
      publish((S)evt.getNewValue()); 
     } 
    } 

    @Override 
    protected abstract void process(List<S> chunks); 
} 

Các lớp này có thể được sử dụng cho tất cả các tiện ích Swing của bạn. Đối với một ProgressBar, các "hét to" sẽ là một Integer, và kiểu trả về là Void:

public class ProgressExample { 
    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
    @Override 
    public void run() { 

     // 1. setup the progress bar 
     final JProgressBar progressBar = new JProgressBar(0, 99); 

     // 2. Wrap the logic in a "Loud Call" 
     LoudCall<Void, Integer> aMethod = new LoudCall<Void, Integer>() { 
      @Override 
      public Void call() throws Exception { 
       for (int i = 0; i < 100; i++) { 
        // "i have an update for the GUI!" 
        shoutOut(i); 
        Thread.sleep(100); 
       } 
       return null; 
      } 
     }; 

     // 3. Run it with a "Listener Task" 
     (new ListenerTask<Void, Integer>(aMethod) { 
      @Override 
      protected void process(List<Integer> chunks) { 
       progressBar.setValue(chunks.get(chunks.size() - 1)); 
      } 
     }).execute(); 

     // 4. show it off! 
     JOptionPane.showOptionDialog(null, 
      new Object[] { "Process", progressBar }, "Process", 
      JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, 
      null, null, null 
     ); 
    } 
     }); 
    } 
} 

Chỉ người nghe cần phải biết gì về các chi tiết GUI, và logic nền vẫn có quyền kiểm soát xuất bản (gián tiếp, bằng cách "la hét"). Mã này có nhiều chi tiết, dễ đọc và có thể sử dụng lại được.

Tôi nhận thấy câu hỏi này khá cũ, nhưng hy vọng nó sẽ giúp ai đó!

+1

Thật khó để đánh giá thấp câu trả lời này hữu ích như thế nào. Nó thưởng cho việc đọc lại! – Arvanem

Các vấn đề liên quan