※ 引述《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)