当单击按钮时,我想在JPanel中将椭圆形从一个位置移动到另一个位置。这是我想出的代码。但是,当我单击该按钮时,所有这些都立即发生而没有明显的移动,从开始到结束的速度都很慢。椭圆形刚出现在新位置。
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.JPanel;
public class testtest implements ActionListener{
JButton button;
MyDrawPanel panel;
int x = 0;
int y = 0;
public static void main(String[]args){
testtest test = new testtest();
test.go();
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
panel = new MyDrawPanel();
button = new JButton("Restart");
button.addActionListener(this);
panel.add(button);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
@Override
public void actionPerformed (ActionEvent e){
for(int i=0;i<130;i++){
x++;
y++;
panel.repaint();
try {
Thread.sleep(100);
} catch(Exception ex) { }
}
}
class MyDrawPanel extends JPanel{
@Override
public void paintComponent(Graphics g){
g.fillOval(x, y, 30, 30);
g.setColor(Color.BLACK);
}
}
}
答案 0 :(得分:1)
Swing是单线程并且不是线程安全的。
在Thread.sleep(100)
中使用ActionListener
阻止了事件分发线程,从而阻止了任何内容的绘制。直到actionPerformed
方法存在之后,新的绘画过程才会发生。
有关更多详细信息,请参见Concurrency in Swing。
Swing也不是线程安全的,这意味着您永远不应从EDT上下文之外对UI进行更改。
最简单的解决方案是利用Swing Timer
,它将允许建立定时定时的回调,这些回调在事件调度线程中执行,但不会阻止EDT。
您还缺少OO的重要概念之一,即封装。 x
/ y
属性实际上应由MyDrawPanel
而非testtest
例如...
import java.awt.Color;
import java.awt.Dimension;
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;
import javax.swing.Timer;
public class testtest implements ActionListener {
JButton button;
MyDrawPanel panel;
public static void main(String[] args) {
testtest test = new testtest();
test.go();
}
public void go() {
JFrame frame = new JFrame("Balloon Balls");
panel = new MyDrawPanel();
button = new JButton("Restart");
button.addActionListener(this);
panel.add(button);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private Timer timer;
public void actionPerformed(ActionEvent e) {
if (timer != null) {
return;
}
timer = new Timer(100, new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
if (panel.update()) {
timer.stop();
timer = null;
}
}
});
timer.start();
}
class MyDrawPanel extends JPanel {
private int xPosy = 0;
private int yPosy = 0;
@Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public boolean update() {
xPosy++;
yPosy++;
repaint();
return xPosy > getWidth() || yPosy > getHeight();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillOval(xPosy, yPosy, 30, 30);
g.setColor(Color.BLACK);
}
}
}
答案 1 :(得分:0)
paintComponent就是这样做的,它绘制面板。最初,面板在起始点x y处绘制椭圆形。按下按钮,窗口将被删除,并在新的XY处重新绘制。
运动是您教计算机的概念。如果我们每秒更新面板多次并缓慢移动x y,我们会产生移动的错觉。
制作一个每10毫秒刷新一次的计时器。每次刷新时,稍微增加x和y值并重新粉刷面板。
答案 2 :(得分:0)
除了在MadProgrammer的answer中对Swing线程问题的解释之外,我还建议通过实现MVC Pattern将gui与控件分开。
这提供了更好的封装,更好的职责分离,并使使用线程进行非编辑处理变得更加容易。
具有一个模型,其中包含视图(gui)所需的所有信息:
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
在这种情况下,添加了同步以允许线程使用该模型。
侦听器定义为:
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
视图就是这样。它实现了Listener
,因此可以侦听Model
的更改:
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
@Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
@Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
}
将它们放在一起:参见以下mvce:它添加了一个控制模型和视图的控制器。
为了方便和简单起见,可以将以下代码复制粘贴到一个名为View.java
的文件中并运行。
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;
/*
* View is just that: a dumb as possible display
*/
public class View implements Listener{
private final JButton button;
private final MyDrawPanel panel;
private final Model model;
public View(Model model) {
this.model = model;
panel = new MyDrawPanel();
button = new JButton("Restart");
panel.add(button);
}
public void go(){
JFrame frame = new JFrame("Balloon Balls");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.add(panel);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel{
@Override
public void paintComponent(Graphics g){
super.paintComponent(g); //always call super
g.fillOval(model.getX(), model.getY(), 30, 30);
g.setColor(Color.BLACK);
}
}
@Override
public void onChange() {
panel.repaint();
}
void addActionListener(ActionListener listener){
button.addActionListener(listener);
}
public static void main(String[]args){
new Controller();
}
}
/*
* A simple interface used to link View and Model
*/
interface Listener {
void onChange();
}
/*
* The model contains the information for the view and information from the view
* The model is independent of the user interface.
* It notifies Listener on changes.
*/
class Model {
private Listener listener;
private int x = 0, y = 0;
synchronized int getX() {return x;}
synchronized void setX(int x) { this.x = x; }
synchronized int getY() {return y;}
synchronized void setY(int y) { this.y = y; }
void setListener(Listener listener){
this.listener = listener;
}
//notify listener when changed
void notifyListener(){
if(listener != null) {
listener.onChange();
}
}
}
/*
* The controller "wires" the view and model, and does the processing.
*/
class Controller implements ActionListener{
private final Model model;
private final View view;
public Controller() {
model = new Model();
view = new View(model);
model.setListener(view);
view.addActionListener(this);
view.go();
}
@Override
public void actionPerformed (ActionEvent e){
new Thread(()->{
for(int i=0;i<130;i++){
model.setX(model.getX()+1);
model.setY(model.getY()+1);
model.notifyListener();
System.out.println(model.getX()+" - "+ model.getY());
try {
Thread.sleep(100);
} catch(Exception ex) { }
}
}).start();
}
}