在JPanel中移动图形

时间:2019-02-26 00:00:06

标签: java graphics

当单击按钮时,我想在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);

        }
    }
}

3 个答案:

答案 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();
    }
}