看板 java 關於我們 聯絡資訊
※ 引述《gp123 (George Peng)》之銘言: : 這個問題很明顯是thread的問題, : 由於Swing元件的更新在另一個thread中進行, : 而這個thread的優先權很低。 : : 我發現函式只會自己跑自己的 : : 但JLabel完全沒有反應 : : 等到遞迴函式跑完了 : : Label才終於顯示最後的狀態 : : 可是中途完全沒有任何反應 : 在你的狀況中, : 幾乎要等到遞迴的method執行完後, : 才有機會輪到Swing一次更新到最新狀況, : 才會造成你看到的結果。 : 通常不建議在一般的worker thread中直接操作Swing, : 長時間的操作中更不合適, : 可試著參考 : http://slientfreaky.blogspot.tw/2011/09/java-swing.html : 的方式, : 將你的函式移到GUI的執行緒中進行。 通常 GUI 回應不夠及時(常見於提問中的說法是:中間變化略過直接顯示最後的 狀態),是由於在 UI Thread 做了耗時間的事,若這些事裡頭包含了操作 UI 組件 或變更組件資料/狀態,就會出現『常見說法』中的情況。 這不是由於 UI Thread 的 priority 太低所致。 原帖主希望在一個遞迴函式的執行過程中由 UI 組件顯示執行的狀態(也許是每一個 call stack 的參數或某些狀態),要這樣子做來幫助了解遞迴函式執行的流程(教學 用的程式?)勢必要在遞迴函式中加入 delay,否則程式執行的速度很快,肉眼是 無法觀察到過程中 UI 組件顯示的即時資訊(人的反應太慢)。 下面是一個以遞迴函式來做倒數的 demo(對,很矯情的 demo),它顯示出一般 要做一件耗時的事情又需要在過程中更新 UI 組件來顯示其 progress 的常見做法。 這只是一個用來點出某些觀念(我在上一篇的推文)的demo,所以暫時請不要講究 這倒數精不精確或其他小細節。 CountDownDemo.java(網頁版: http://ideone.com/VEY1io ) CountDown.java(網頁版: http://ideone.com/edwtgI ) *減少看官翻頁動作,註解(討論)寫在前頭,程式碼殿後 如果把 start 按鈕按下後的動作改成 counter.syncStart(10); 取代了原本的 counter.asyncStart(10); 這就是在 UI Thread(event-dispatching thread) 裡做耗時的工作(倒數需要 好幾秒),會導致倒數的期間(約 10 秒) GUI 像是死了一般(不會更新);直到 倒數完畢才直接顯示 0 秒。 =========================================================================== CountDown.java package cc.ptt; public class CountDown { public interface OnTickListener { public void onTick(int seconds); } private static class NullTickListener implements OnTickListener { @Override public void onTick(int seconds) { } } private static final OnTickListener nullListener = new NullTickListener(); private OnTickListener tickListener = nullListener; public void asyncStart(final int seconds) { // run in some worker thread new Thread(new Runnable() { public void run() { try { count(tickListener, seconds); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } public void syncStart(int seconds) { // run in the caller thread try { count(tickListener, seconds); } catch (InterruptedException e) { e.printStackTrace(); } } private void count(OnTickListener listener, int timeInSecs) throws InterruptedException { if (timeInSecs <= 0) { listener.onTick(0); return; } listener.onTick(timeInSecs); Thread.sleep(1000); count(listener, timeInSecs - 1); } public OnTickListener getTickListener() { return tickListener == nullListener ? null : tickListener; } public void setTickListener(OnTickListener tickListener) { if (tickListener == null) this.tickListener = nullListener; else this.tickListener = tickListener; } } =========================================================================== CountDownDemo.java package cc.ptt; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class CountDownDemo { public static void main(String[] args) { JFrame frm = new JFrame("Demo for Java@ptt"); frm.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); final JLabel messageBar = new JLabel("", JLabel.CENTER); messageBar.setBorder(BorderFactory.createLoweredBevelBorder()); messageBar.setFont(messageBar.getFont().deriveFont(48f)); frm.getContentPane().add(messageBar); final JButton start = new JButton("開始倒數 10 秒"); frm.getContentPane().add(start, BorderLayout.SOUTH); final CountDown counter = new CountDown(); counter.setTickListener(new CountDown.OnTickListener() { @Override public void onTick(int seconds) { // setText method is thread-safe messageBar.setText(String.format("%d 秒", seconds)); if (seconds <= 0) { SwingUtilities.invokeLater(new Runnable() { public void run() { start.setEnabled(true); } }); } } }); start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { start.setEnabled(false); counter.asyncStart(10); // counter.syncStart(10); } }); frm.setSize(400, 400); frm.setLocationRelativeTo(null); frm.setVisible(true); } } -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 1.172.190.207 ※ 編輯: sbrhsieh 來自: 1.172.187.77 (01/04 01:31)