Java Swing - 调用paintComponent并不清除JPanel?

时间:2014-07-03 02:02:29

标签: java swing timer jpanel paintcomponent

我正在研究一个小型2D游戏引擎,只是为了试验一些设计思路。 我喜欢实体被赋予渲染自己和注册自己的听众的负担的想法 - 实体或“演员”应该定义它自己的所有行为。

问题是,我正在画的JPanel是“涂抹”。描述它的最简单的方法是给你们一个SSCCE,你是如此倾向。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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

public class SSCCE_BufferImageRender {

    public static void main(String[] args) {
        SSCCE_BufferImageRender sbim = new SSCCE_BufferImageRender();
        Environment e = sbim.new Environment();
        Stage s = sbim.new Stage(e);
        e.addStage(s);
        e.setVisible(true);
        s.addActor(sbim.new Actor(s));
    }

    class Environment extends JFrame implements Runnable{

    private static final long serialVersionUID = 1L;
    private ScheduledExecutorService mainService = Executors.newSingleThreadScheduledExecutor();
    private List<Stage> environmentStages;
    private Stage activeStage;

        public Environment() {
            super();
            setSize(new Dimension(400,400));
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            Timer timer = new Timer(17, new ActionListener() {
                public void actionPerformed(ActionEvent arg0) {
                    run();
                }
            });
            environmentStages = new ArrayList<Stage>();
            timer.start();
        }

        @Override
        public void run() {
            if(activeStage != null)
                activeStage.act();
        }

        public void addStage(Stage stage) {
            if(environmentStages.size() == 0) {
                activeStage = stage;
                setContentPane(stage);
            }

            environmentStages.add(stage);
        }

        public KeyListener[] getKeyListeners() {
            return getListeners(KeyListener.class);
        } 
    }

    /**
     * Stage - Basically where everything will be painted
     * */
    class Stage extends JPanel {
        private Environment stageEnvironment;   
        private List<Actor> stageActors;
        private int stageHeight;
        private int stageWidth;

            public Stage(Environment environment) {
                super();
                stageEnvironment = environment;
                stageHeight= stageEnvironment.getHeight();
                stageWidth = stageEnvironment.getWidth();
                this.setSize(stageWidth, stageHeight);
                stageActors = new ArrayList<Actor>();
            }

            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                for(Actor actor : stageActors) {
                    BufferedImage bi = actor.render();
                    g2d.drawImage(bi, 0, 0, null);
                }
                g2d.drawString("Hello World!", 50, 50);
                g2d.dispose();
            }

            public void act() {
                for(Actor actor : stageActors) {
                    actor.act();
                }
                repaint();
            }

            public void addActor(Actor actor){
                stageActors.add(actor);
            }

            public Environment getStageEnvironment() {
                return stageEnvironment;
            }

            public int getWidth() {
                return stageWidth;
            }

            public int getHeight() {
                return stageHeight;
            }

            public void setStageEnvironment(Environment stageEnvironment) {
                this.stageEnvironment = stageEnvironment;
            }
    }

    class Actor {
        private String actorID;
        private Stage actorStage;
        private BufferedImage actorImage;
        private int x,y,radius;
            public Actor(Stage stage) {
                actorStage = stage;
                x = y = radius = 20;
                setActorImage(new BufferedImage(actorStage.getWidth(), actorStage.getHeight(), BufferedImage.TYPE_INT_ARGB));
                if(getKeyListener() != null) {
                    System.out.println("Adding new listener from actor");
                    stage.getStageEnvironment().addKeyListener(getKeyListener());
                }
            }

            public Stage getActorStage() {
                return actorStage;
            }

            public void setActorStage(Stage actorStage) {
                this.actorStage = actorStage;
            }

            public BufferedImage getActorImage() {
                return actorImage;
            }

            public void setActorImage(BufferedImage actorImage) {
                this.actorImage = actorImage;
            }

            public void act(){
                // Do nothing
            }

            public BufferedImage render() {
                Graphics2D g2d = getActorImage().createGraphics();
                g2d.setColor(Color.red);
                g2d.fillOval(x, y, radius, radius);
                g2d.dispose();
                return getActorImage();
            }

            private KeyListener getKeyListener(){
                return new KeyListener() {

                    @Override
                    public void keyPressed(KeyEvent arg0) {}

                    @Override
                    public void keyReleased(KeyEvent arg0) {
                        switch(arg0.getKeyChar()) {
                        case '4': x-=5;break;
                        case '6': x+=5;break;
                        case '8': y-=5;break;
                        case '2': y+=5;break;
                        default:break;
                        }
                    }

                    @Override
                    public void keyTyped(KeyEvent arg0) {}
                };
            }
    }
}

你会注意到我正在使用一个Timer来模拟一个线程...我也有一些代码,我使用的是ScheduledExecutor,但是我看到其他地方弄乱了EDT如何处理Swing组件会导致问题。

那么,任何想法为什么我的JPanel没有正确绘画?

1 个答案:

答案 0 :(得分:1)

问题在于你Actor ...

public BufferedImage render() {
    Graphics2D g2d = getActorImage().createGraphics();
    g2d.setColor(Color.red);
    g2d.fillOval(x, y, radius, radius);
    g2d.dispose();
    return getActorImage();
}

在这里,您将获得Graphics的{​​{1}}上下文并为其绘画......但您还没有删除之前绘制到BufferedImage的内容,所以它只是&#34;添加&#34;它。

虽然无法清除BufferedImage,但更好的解决方案是更改渲染的方式。

而不是BufferedImage呈现给它自己的Actor,当你扩展它时,会给你的记忆带来负担,你应该传递{的引用{1}}要绘制的上下文,例如......

BufferedImage

Graphics方法中,只需将组件public void render(Graphics2D g2d) { g2d.setColor(Color.red); g2d.fillOval(x, y, radius, radius); } 传递给它......

paintComponent

如果您担心Graphics@Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); for (Actor actor : stageActors) { actor.render(g2d); } g2d.drawString("Hello World!", 50, 50); g2d.dispose(); } 上下文做了一些奇怪的事情,您可以为每个内容创建一个单独的副本....

Actor

此外,您的Graphics方法没有理由成为@Override protected void paintComponent(Graphics g) { super.paintComponent(g); for (Actor actor : stageActors) { Graphics2D g2d = (Graphics2D) g.create(); actor.render(g2d); g2d.dispose(); } g.drawString("Hello World!", 50, 50); } ,因为您从不希望任何人直接调用它。