作者sbrhsieh (sbr)
看板java
标题Re: [问题] Panel的paintComponent并未确实被呼叫
时间Sun Jun 28 16:17:46 2009
※ 引述《issuemylove (skill)》之铭言:
: 大家好^^
: 在下最近稍微研究了一下User interface的部份
: 想说试着让某图形(圆形)在视窗上位移
: 如在右方的连结情况下 http://nopaste.info/3b91c2304d.html
: 图形正常的移动了
: 但是我使用 Button控制它的移动时机时(即按下button後才移动)
: 如右方连结 http://nopaste.info/4cb008e3bf.html
: 却出现了中间移动过程皆不出现的情况
: 而直接时间到後,才将最後一个画面印出
: 请问这是哪边出了问题呢?
: 谢谢大家的回答
repaint 是个 asynchronous operation,(大致上来说)它的任务可看成是 queue
一个 PaintEvent 到 event-dispatching thread。他并不是直接造成元件的
paint routine 被执行来绘制外观。此外过多的 PaintEvent 累积在 event-
dispatching thread 里可能会被合并,因此并不是每 invoke 一次就一定会令元件
repaint 一次。
你的程式在 event-dispatching thread 执行
把多个 paint event queue 到
event-dispatching thread 里的动作(A),在 A 完成以前 event-dispatching
thread(EDT) 不会有机会去处理 paint event。等到 A 完成了,EDT 可以处理已累积
的 paint event,就算 EDT 有照实一个 paint event 做一次 UI 上的绘图,你看起来
的效果也会是直接看到 UI component 最後的状态时外观(在 30 ms 内,萤幕上如果有
许多变化,你可能也看不出来)。
整个程式并不是如你想的 repaint method 执行一次 UI 元件外观更新一次,停个
100 ms 再继续。而会是程式先停了 10 秒,然後突然执行了 100 次绘图动作,或是
一次(两者你用肉眼观察或许感觉上是相同的)。
你的程式要有动画的效果,主要可以往两个方向来改。
1. 在 event-dispatching thread 里改变的 UI component 状态後,invoke UI
component 的 paintImmediately method 来直接触发 paint routine 来更新外观。
例如把
frame.repaint();
改成执行 myPanel instance 的 paintImmediately method。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class myCircleMove {
JFrame frame;
JButton button;
myPanel panel;
int x;
int y;
public static void main(String[] args) {
myCircleMove gui = new myCircleMove();
gui.go();
}
public void go() {
x = 10;
y = 10;
panel = new myPanel();
button = new JButton("click me");
button.addActionListener(new myActionListener());
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(600, 600);
frame.setVisible(true);
}
public class myActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < 100; i++) {
x++;
y++;
// frame.repaint(); // <= 怪怪的
panel.paintImmediately(0, 0,
panel.getWidth(), panel.getHeight());
try {
Thread.sleep(100);
}
catch (Exception e1) {
}
}
}
}
class myPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.red);
g.fillOval(x, y, 40, 40);
}
}
}
2. 把变更 UI component 状态的动作由 event-dispatching thread 以外的
thread 来执行。
public class myActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 100; i++) {
x++;
y++;
// frame.repaint(); // <= 怪怪的
panel.repaint();
try {
Thread.sleep(100);
}
catch (Exception e1) {
}
}
}
}).start();
}
}
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 218.173.139.219
※ 编辑: sbrhsieh 来自: 218.173.139.219 (06/28 17:06)
1F:推 issuemylove:谢谢你的答覆~ 06/28 17:27
2F:→ issuemylove:可是我不明白为何使用Button的方法,此问题会浮现?..? 06/28 17:29
3F:→ issuemylove:照理说,没用Button(即直接移动)也会有该问题?@@ 06/28 17:31
没有使用 button 触发动画的版本中,move100 method 不是在 EDT 里执行,而是在
main thread 里。move100 method 执行过程中 EDT 有机会去处理 repaint 安排的
paint event(导致 EDT 去执行 UI component 的 paint routine)。如果在 go
method 里把 move100 invocation 丢到 EDT 去跑,就会出现相同问题。
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
move100();
}
}
※ 编辑: sbrhsieh 来自: 218.173.139.219 (06/28 17:57)
4F:推 issuemylove:感谢s大的解述^^ 我已经甚有了解此问题了,谢谢 06/28 18:08