java - 借鉴其他类的 JPanel

标签 java swing

在我的程序中,我尝试在按下鼠标时在 JPanel 上绘画。 mousePressed 方法只是为了测试另一个类的绘画。稍后,spawn 方法将被其他类方法调用。当我按下鼠标按钮时,会调用 spawnPedestrian() ,但不会绘制 Pedestrian 。下面是一个运行示例,其中包含我的项目中的代码。如果您创建一个项目 Roundabout 并将此代码粘贴到其中,您应该能够运行它(图像是热链接的)。 如何修复 spawnPedestrian() 方法?

public class Roundabout extends JFrame {

public static Surface surface;

public Roundabout() {
    initUI();
}

private void initUI() {
    setTitle("Roundabout");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    surface = new Surface();
    add(surface); 
    this.addMouseListener(new MouseAdapter() {// empty implementation of all
        // MouseListener`s methods
        @Override
        public void mousePressed(MouseEvent e) {
            //Spawn
            Spawn sp = new Spawn();
            sp.spawnPedestrian(300, 100);
        }
    });

    setSize(1618, 850);
    setLocationRelativeTo(null);
}

public static JPanel getSurface() {
    return surface;
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            Roundabout roundabout = new Roundabout();
            roundabout.setVisible(true);
        }
    });
}

//Track class
class Track {

    BufferedImage track;
    Point trackPosition;
    Point TRACK_POS = new Point(0, 0);

    public Track() {
        try {
            track = ImageIO.read(new URL("/image/2U3j5.png"));
        } catch (Exception ex) {
            System.out.println("Problem loading track image: " + ex);
        }
        trackPosition = new Point(TRACK_POS.x, TRACK_POS.y);
    }

    public void paint(Graphics g) {
        g.drawImage(track, TRACK_POS.x, TRACK_POS.y, null);
    }

}

//Surface class
public class Surface extends JPanel {

    Track track = new Track();

    public List<Vehicle> toDraw = new ArrayList<>();

    public Surface() {
        Pedestrian p = new Pedestrian(100, 100);
        toDraw.add(p);
    }

    @Override
    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        //setLayout(null);
        track.paint(g);
        //Make sure the track is painted first
        for (Vehicle v : toDraw) {
            v.paint(g);
        }

    }

}

class Pedestrian extends Vehicle {

    BufferedImage pedestrian;
    Point pedestrianPosition;
    double pedestrianRotation = 0;
    int pedestrianW, pedestrianH;

    public Pedestrian(int x, int y) {
        try {
            pedestrian = ImageIO.read(new URL("/image/wm0I5.png"));
        } catch (IOException e) {
            System.out.println("Problem loading pedestrian images: " + e);
        }

        pedestrianPosition = new Point(x, y);
        pedestrianW = pedestrian.getWidth();
        pedestrianH = pedestrian.getHeight();
    }

    @Override
    public void paint(Graphics g) {

        Graphics2D g2d = (Graphics2D) g;

        g2d.rotate(Math.toRadians(pedestrianRotation), pedestrianPosition.x, pedestrianPosition.y);

        g2d.drawImage(pedestrian, pedestrianPosition.x, pedestrianPosition.y, null);

    }

    @Override
    public void setPath(List<Point> path) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void update(double i) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
}

//Spawn class
class Spawn {

    public void spawnPedestrian(int x, int y) {
        //Create a new pedestrian.        

        System.out.println("Spawn a pedestrian.");
        Pedestrian p = new Pedestrian(x, y);

        Roundabout.surface.toDraw.add(p);
        Roundabout.surface.revalidate();
        Roundabout.surface.repaint();            

    }
}

public abstract class Vehicle {

    public abstract void setPath(List<Point> path);

    public abstract void update(double i);

    public abstract void paint(Graphics g);
    }
}

编辑:现在可以通过鼠标单击生成Pedestrian

最佳答案

基本上,您希望解耦代码并将责任集中到类中。因此,“数据”应该由某种模型来维护,渲染应该由某种 View 来处理,模型和 View 的更新应该由某种 Controller 来处理。

这使得更换任何一个部分变得更加容易,而不需要一大堆新代码或其他更改。这还意味着每个类都有一个定义的责任范围,并且阻止您尝试从 View 内更改应由模型处理的状态(这可能会使状态陷入困惑)

让我们从要绘制的东西开始

public interface Sprite {

    public void paint(Graphics2D g2d);

}

public interface MoveableSprite extends Sprite {

    public void update(Container container);

}

它们代表静态 Sprite (例如一棵树)或移动的 Sprite (并且想要定期更新)

这些包含在模型中

public interface GameModel {

    public List<Sprite> getSprites();

    public void setObserver(Observer<MoveableSprite> observer);

    public Observer<MoveableSprite> getObserver();

    public void spawnSprite();

}

它提供了一些方法来通知(在本例中为单个)感兴趣的各方有关某种状态更改的信息。对于此示例,这意味着新的 MoveableSprite 已可用

Observer 非常基本,只有一个回调...

public interface Observer<T> {

    public void stateChanged(T parent);

}

还有一个“引擎”来帮助驱动它......

public class GameEngine {

    private GameModel model;
    private SurfacePane surface;
    private Timer timer;

    public GameEngine(GameModel model, SurfacePane surface) {
        this.model = model;
        this.surface = surface;

        model.setObserver(new Observer<MoveableSprite>() {
            @Override
            public void stateChanged(MoveableSprite sprite) {
                sprite.update(getSurface());
            }
        });

        timer = new Timer(40, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (Sprite sprite : getModel().getSprites()) {
                    if (sprite instanceof MoveableSprite) {
                        ((MoveableSprite) sprite).update(getSurface());
                    }
                }
                getSurface().repaint();
            }
        });
    }

    public GameModel getModel() {
        return model;
    }

    public SurfacePane getSurface() {
        return surface;
    }

    public void start() {
        timer.start();
    }

    public void stop() {
        timer.stop();
    }

}

这是一个非常基本的示例,但基本上,它更新了 MoveableSprite 的位置并要求表面重新绘制自身。它还会观察 GameModel 中的任何新 Sprite ,并会立即更新它们的位置,因此它们不会出现在某些“奇怪”的地方

好吧,现在我们实际上需要实现其中一些才能使其正常工作

public class DefaultGameModel implements GameModel {

    private Observer<MoveableSprite> observer;
    private List<Sprite> sprites;

    public DefaultGameModel() {
        sprites = new ArrayList<>(25);
        for (int index = 0; index < 10; index++) {
            spawnSprite();
        }
    }

    @Override
    public List<Sprite> getSprites() {
        return Collections.unmodifiableList(sprites);
    }

    public void spawnSprite() {
        try {
            ZombieSprite sprite = new ZombieSprite();
            sprites.add(sprite);

            Observer<MoveableSprite> observer = getObserver();
            if (observer != null) {
                observer.stateChanged(sprite);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setObserver(Observer<MoveableSprite> observer) {
        this.observer = observer;
    }

    @Override
    public Observer<MoveableSprite> getObserver() {
        return observer;
    }

}

public class ZombieSprite implements MoveableSprite {

    private int x;
    private int y;

    private int xDelta;
    private int yDelta;

    private BufferedImage img;

    private Observer<Sprite> observer;
    private boolean initialised = false;

    public ZombieSprite() throws IOException {
        img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
    }

    @Override
    public void update(Container container) {
        if (!initialised) {
            x = (int) (Math.random() * container.getWidth());
            y = (int) (Math.random() * container.getHeight());

            Random rnd = new Random();
            xDelta = rnd.nextBoolean() ? 1 : -1;
            yDelta = rnd.nextBoolean() ? 1 : -1;
            initialised = true;
        }
        x += xDelta;
        y += yDelta;

        if (x < 0) {
            x = 0;
            xDelta *= -1;
        } else if (x + img.getWidth() > container.getWidth()) {
            x = container.getWidth() - img.getWidth();
            xDelta *= -1;
        }
        if (y < 0) {
            y = 0;
            yDelta *= -1;
        } else if (y + img.getHeight() > container.getHeight()) {
            y = container.getHeight() - img.getHeight();
            yDelta *= -1;
        }
    }

    @Override
    public void paint(Graphics2D g2d) {
        g2d.drawImage(img, x, y, null);
    }

}

这两个类实现了 GameModelMoveableSprite 接口(interface)。我们使用接口(interface)来解耦代码,这使得改变事物的工作方式变得更容易,并为商定的契约(Contract)和实现的异常(exception)提供了一个起点

最后,一些真正描绘当前状态的东西......

public class SurfacePane extends JPanel {

    private GameModel model;

    public SurfacePane(GameModel model) {
        this.model = model;

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                getModel().spawnSprite();
            }
        });
    }

    public GameModel getModel() {
        return model;
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(200, 200);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g.create();
        GameModel model = getModel();
        for (Sprite sprite : model.getSprites()) {
            sprite.paint(g2d);
        }
        g2d.dispose();
    }

}

您不会发现此类具有 MouseListener,这是故意的,因为可能添加到此容器的其他组件可能会阻止 MouseListener已通知,所以不要这样做。但是 MouseListener 只是调用模型来生成另一个僵尸......

最后,我们需要彻底探索它......

GameModel model = new DefaultGameModel();
SurfacePane surfacePane = new SurfacePane(model);
GameEngine engine = new GameEngine(model, surfacePane);

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(surfacePane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

engine.start();

因为我知道这是很多不连贯的概念需要放在一起,所以这是一个完整的例子......

import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeListener;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                GameModel model = new DefaultGameModel();
                SurfacePane surfacePane = new SurfacePane(model);
                GameEngine engine = new GameEngine(model, surfacePane);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(surfacePane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                engine.start();
            }
        });
    }

    public class SurfacePane extends JPanel {

        private GameModel model;

        public SurfacePane(GameModel model) {
            this.model = model;

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    getModel().spawnSprite();
                }
            });
        }

        public GameModel getModel() {
            return model;
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            GameModel model = getModel();
            for (Sprite sprite : model.getSprites()) {
                sprite.paint(g2d);
            }
            g2d.dispose();
        }

    }

    public class GameEngine {

        private GameModel model;
        private SurfacePane surface;
        private Timer timer;

        public GameEngine(GameModel model, SurfacePane surface) {
            this.model = model;
            this.surface = surface;

            model.setObserver(new Observer<MoveableSprite>() {
                @Override
                public void stateChanged(MoveableSprite sprite) {
                    sprite.update(getSurface());
                }
            });

            timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Sprite sprite : getModel().getSprites()) {
                        if (sprite instanceof MoveableSprite) {
                            ((MoveableSprite) sprite).update(getSurface());
                        }
                    }
                    getSurface().repaint();
                }
            });
        }

        public GameModel getModel() {
            return model;
        }

        public SurfacePane getSurface() {
            return surface;
        }

        public void start() {
            timer.start();
        }

        public void stop() {
            timer.stop();
        }

    }

    public interface Observer<T> {

        public void stateChanged(T parent);

    }

    public interface Sprite {

        public void paint(Graphics2D g2d);

    }

    public interface MoveableSprite extends Sprite {

        public void update(Container container);

    }

    public interface GameModel {

        public List<Sprite> getSprites();

        public void setObserver(Observer<MoveableSprite> observer);

        public Observer<MoveableSprite> getObserver();

        public void spawnSprite();

    }

    public class DefaultGameModel implements GameModel {

        private Observer<MoveableSprite> observer;
        private List<Sprite> sprites;

        public DefaultGameModel() {
            sprites = new ArrayList<>(25);
            for (int index = 0; index < 10; index++) {
                spawnSprite();
            }
        }

        @Override
        public List<Sprite> getSprites() {
            return Collections.unmodifiableList(sprites);
        }

        public void spawnSprite() {
            try {
                ZombieSprite sprite = new ZombieSprite();
                sprites.add(sprite);

                Observer<MoveableSprite> observer = getObserver();
                if (observer != null) {
                    observer.stateChanged(sprite);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void setObserver(Observer<MoveableSprite> observer) {
            this.observer = observer;
        }

        @Override
        public Observer<MoveableSprite> getObserver() {
            return observer;
        }

    }

    public class ZombieSprite implements MoveableSprite {

        private int x;
        private int y;

        private int xDelta;
        private int yDelta;

        private BufferedImage img;

        private Observer<Sprite> observer;
        private boolean initialised = false;

        public ZombieSprite() throws IOException {
            img = ImageIO.read(getClass().getResource("/LogoZombi.png"));
        }

        @Override
        public void update(Container container) {
            if (!initialised) {
                x = (int) (Math.random() * container.getWidth());
                y = (int) (Math.random() * container.getHeight());

                Random rnd = new Random();
                xDelta = rnd.nextBoolean() ? 1 : -1;
                yDelta = rnd.nextBoolean() ? 1 : -1;
                initialised = true;
            }
            x += xDelta;
            y += yDelta;

            if (x < 0) {
                x = 0;
                xDelta *= -1;
            } else if (x + img.getWidth() > container.getWidth()) {
                x = container.getWidth() - img.getWidth();
                xDelta *= -1;
            }
            if (y < 0) {
                y = 0;
                yDelta *= -1;
            } else if (y + img.getHeight() > container.getHeight()) {
                y = container.getHeight() - img.getHeight();
                yDelta *= -1;
            }
        }

        @Override
        public void paint(Graphics2D g2d) {
            g2d.drawImage(img, x, y, null);
        }

    }

}

关于java - 借鉴其他类的 JPanel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30342509/

相关文章:

java - 如何从 JTable 中删除一行

java - 相同的代码,通过 Lotus Domino 发送电子邮件的不同行为

java - 如何在 CMD 中运行小程序而不创建两个单独的文件(即 .html 和 .java 文件)。 html代码嵌入到java代码中

java - 在 HSQLDB jar 中调用 DatabaseManagerSwing 类的 main 方法后如何获取窗口关闭事件?

java - 具有工作队列场景的 RabbitMQ 中的 channel 空闲

java - 如何根据窗口大小缩放对象?

java - 将图像复制到新目录并重命名 - Java

java - JTextField 和 RMI Java 的 GUI 问题

java - dispose() 的问题;

java - JFrame 点绘图的奇怪行为