Java 我能';我看不到圆在移动

Java 我能';我看不到圆在移动,java,swing,awt,Java,Swing,Awt,在java中使用Swing时,我试图在单击按钮时将圆从起始位置缓慢移动到结束位置。然而,我看不到圆圈在移动。它只是在瞬间从开始移动到结束 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class MyApp { private int x = 10; private int y = 10; private JFrame f; private MyDraw m;

在java中使用Swing时,我试图在单击按钮时将圆从起始位置缓慢移动到结束位置。然而,我看不到圆圈在移动。它只是在瞬间从开始移动到结束

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MyApp {

    private int x = 10;
    private int y = 10;
    private JFrame f;
    private MyDraw m;
    private JButton b;

    public void go() {
        f = new JFrame("Moving circle");
        b = new JButton("click me to move circle");
        m = new MyDraw();
        f.add(BorderLayout.SOUTH, b);

        f.add(BorderLayout.CENTER, m);
        f.setSize(500, 500);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);

        b.addActionListener(new Bute());
    }

    public static void main(String[] args) {
        MyApp m = new MyApp();
        m.go();
    }

    private class Bute implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            for (int i = 0; i < 150; i++) {
                ++x;
                ++y;
                m.repaint();
                Thread.sleep(50);
            }
        }
    }

    private class MyDraw extends JPanel {
        @Override
        public void paintComponent(Graphics g) {
            g.setColor(Color.white);
            g.fillRect(0, 0, 500, 500);
            g.setColor(Color.red);
            g.fillOval(x, y, 40, 40);
        }
    }
}
import javax.swing.*;
导入java.awt.*;
导入java.awt.event.*;
公共类MyApp{
私人整数x=10;
私人智力y=10;
私有jf框架;
私人密室;
私人JButton b;
公开作废go(){
f=新JFrame(“移动圆”);
b=新的JButton(“单击我移动圆圈”);
m=新的MyDraw();
f、 添加(BorderLayout.SOUTH,b);
f、 添加(BorderLayout.CENTER,m);
f、 设置大小(500500);
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 setVisible(真);
b、 addActionListener(新Bute());
}
公共静态void main(字符串[]args){
MyApp m=新的MyApp();
m、 go();
}
私有类Bute实现ActionListener{
@凌驾
已执行的公共无效操作(操作事件e){
对于(int i=0;i<150;i++){
++x;
++y;
m、 重新油漆();
睡眠(50);
}
}
}
私有类MyDraw扩展了JPanel{
@凌驾
公共组件(图形g){
g、 setColor(Color.white);
g、 fillRect(0,0500500);
g、 setColor(Color.red);
g、 椭圆形(x,y,40,40);
}
}
}

我认为问题在于动作监听器,因为当我不使用按钮的时候,它就工作了。有什么建议吗?

正如Andrew Thompson所说,调用
Thread.sleep()
而不定义第二个线程会冻结所有内容,因此解决方案是定义并运行另一个线程,如下所示:

class Bute implements ActionListener, Runnable {
    //let class implement Runnable interface
    Thread t;   // define 2nd thread

    public void actionPerformed(ActionEvent e) {

        t = new Thread(this);   //start a new thread
        t.start();
    }

    @Override               //override our thread's run() method to do what we want 
    public void run() {     //this is after some java-internal init stuff called by start()
        //b.setEnabled(false);
        for (int i = 0; i < 150; i++) {
            x++;
            y++;
            m.repaint();
            try {
                Thread.sleep(50);   //let the 2nd thread sleep
            } catch (InterruptedException iEx) {
                iEx.printStackTrace();  
            }
        }
        //b.setEnabled(true);
    }

}
class Bute实现ActionListener,可运行{
//让类实现可运行接口
线程t;//定义第二个线程
已执行的公共无效操作(操作事件e){
t=新线程(this);//启动新线程
t、 start();
}
@Override//Override我们线程的run()方法来执行我们想要的操作
public void run(){//这是在start()调用的一些java内部init之后
//b、 setEnabled(假);
对于(int i=0;i<150;i++){
x++;
y++;
m、 重新油漆();
试一试{
Thread.sleep(50);//让第二个线程睡眠
}捕获(中断异常iEx){
iEx.printStackTrace();
}
}
//b、 setEnabled(真);
}
}

此解决方案的唯一问题是,多次按下按钮将加快圆的速度,但可以通过
b.setEnabled(true/false)
在动画期间使按钮不可点击来解决此问题。这不是最好的解决方案,但它很有效。

正如Andrew Thompson所说,在不定义第二个线程的情况下调用
Thread.sleep()
会冻结所有内容,因此解决方案是定义并运行另一个线程,如下所示:

class Bute implements ActionListener, Runnable {
    //let class implement Runnable interface
    Thread t;   // define 2nd thread

    public void actionPerformed(ActionEvent e) {

        t = new Thread(this);   //start a new thread
        t.start();
    }

    @Override               //override our thread's run() method to do what we want 
    public void run() {     //this is after some java-internal init stuff called by start()
        //b.setEnabled(false);
        for (int i = 0; i < 150; i++) {
            x++;
            y++;
            m.repaint();
            try {
                Thread.sleep(50);   //let the 2nd thread sleep
            } catch (InterruptedException iEx) {
                iEx.printStackTrace();  
            }
        }
        //b.setEnabled(true);
    }

}
class Bute实现ActionListener,可运行{
//让类实现可运行接口
线程t;//定义第二个线程
已执行的公共无效操作(操作事件e){
t=新线程(this);//启动新线程
t、 start();
}
@Override//Override我们线程的run()方法来执行我们想要的操作
public void run(){//这是在start()调用的一些java内部init之后
//b、 setEnabled(假);
对于(int i=0;i<150;i++){
x++;
y++;
m、 重新油漆();
试一试{
Thread.sleep(50);//让第二个线程睡眠
}捕获(中断异常iEx){
iEx.printStackTrace();
}
}
//b、 setEnabled(真);
}
}

此解决方案的唯一问题是,多次按下按钮将加快圆的速度,但可以通过
b.setEnabled(true/false)
在动画期间使按钮不可点击来解决此问题。这不是最好的解决方案,但很有效。

正如评论和另一个答案中所说的,不要阻止EDT
Thead.sleep(…)
将阻止它,因此您有两个选项:

  • 创建和管理您自己的(新)线程
  • 挥杆
在这个答案中,我将使用Swing
计时器,因为它更容易使用。我还将
paintComponent
方法更改为使用
Shape
API,并相应地将按钮文本更改为开始和停止,以及对按钮和计时器重复使用相同的
ActionListener

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MovingCircle {
    private JFrame frame;
    private CustomCircle circle;
    private Timer timer;
    private JButton button;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
    }

    private void createAndShowGui() {
        frame = new JFrame(this.getClass().getSimpleName());
        circle = new CustomCircle(Color.RED);
        timer = new Timer(100, listener);
        button = new JButton("Start");
        button.addActionListener(listener);

        circle.setBackground(Color.WHITE);
        frame.add(circle);
        frame.add(button, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private ActionListener listener = (e -> {
        if (!timer.isRunning()) {
            timer.start();
            button.setText("Stop");
        } else {
            if (e.getSource().equals(button)) {
                timer.stop();
                button.setText("Start");
            }
        }
        circle.move(1, 1);
    });

    @SuppressWarnings("serial")
    class CustomCircle extends JPanel {
        private Color color;
        private int circleX;
        private int circleY;

        public CustomCircle(Color color) {
            this.color = color;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(color);
            g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
        }

        @Override
        public Dimension preferredSize() {
            return new Dimension(100, 100);
        }

        public void move(int xGap, int yGap) {
            circleX += xGap;
            circleY += yGap;
            revalidate();
            repaint();
        }

        public int getCircleX() {
            return circleX;
        }

        public void setCircleX(int circleX) {
            this.circleX = circleX;
        }

        public int getCircleY() {
            return circleY;
        }

        public void setCircleY(int circleY) {
            this.circleY = circleY;
        }
    }
}

很抱歉,我无法发布我想要的GIF,但此示例按预期运行。

如评论和另一个答案中所述,不要阻止EDT
Thead.sleep(…)
将阻止它,因此您有两个选项:

  • 创建和管理您自己的(新)线程
  • 挥杆
在这个答案中,我将使用Swing
计时器,因为它更容易使用。我还将
paintComponent
方法更改为使用
Shape
API,并相应地将按钮文本更改为开始和停止,以及对按钮和计时器重复使用相同的
ActionListener

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MovingCircle {
    private JFrame frame;
    private CustomCircle circle;
    private Timer timer;
    private JButton button;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new MovingCircle()::createAndShowGui);
    }

    private void createAndShowGui() {
        frame = new JFrame(this.getClass().getSimpleName());
        circle = new CustomCircle(Color.RED);
        timer = new Timer(100, listener);
        button = new JButton("Start");
        button.addActionListener(listener);

        circle.setBackground(Color.WHITE);
        frame.add(circle);
        frame.add(button, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private ActionListener listener = (e -> {
        if (!timer.isRunning()) {
            timer.start();
            button.setText("Stop");
        } else {
            if (e.getSource().equals(button)) {
                timer.stop();
                button.setText("Start");
            }
        }
        circle.move(1, 1);
    });

    @SuppressWarnings("serial")
    class CustomCircle extends JPanel {
        private Color color;
        private int circleX;
        private int circleY;

        public CustomCircle(Color color) {
            this.color = color;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(color);
            g2d.fill(new Ellipse2D.Double(circleX, circleY, 50, 50));
        }

        @Override
        public Dimension preferredSize() {
            return new Dimension(100, 100);
        }

        public void move(int xGap, int yGap) {
            circleX += xGap;
            circleY += yGap;
            revalidate();
            repaint();
        }

        public int getCircleX() {
            return circleX;
        }

        public void setCircleX(int circleX) {
            this.circleX = circleX;
        }

        public int getCircleY() {
            return circleY;
        }

        public void setCircleY(int circleY) {
            this.circleY = circleY;
        }
    }
}

很抱歉,我无法发布我想要的GIF,但此示例按预期运行。

我推荐一位眼镜商。不要阻止EDT(事件调度线程)。发生这种情况时,GUI将“冻结”。有关详细信息和修复方法,请参阅。我推荐一位眼镜商。不要阻止EDT(事件调度线程)。发生这种情况时,GUI将“冻结”。有关详细信息和修复方法,请参见。
public void go(){JFrame frame=new JFrame();frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);MyDrawPanel drawPanel=new MyDrawPanel();frame.setSize(300300);frame.setVisible(true);用于