2

我完全被这个程序遇到的一个问题所困扰,我必须用秋千画一个城市。基本上我想要做的是让窗户不会改变每一帧。我已经尝试了几乎所有我能想到的东西,但还没有任何效果。

这是绘制所有内容的主要类

import java.applet.Applet;
import java.awt.*;
import java.util.*;
import javax.swing.JFrame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

public class Skyline extends JFrame implements MouseMotionListener
{
    private int mX, mY; //Mouse cooddinates
    private Image mImage; //Image buffer
    //private Image mImage2; //Image buffer

    private int num = 0;

    private Building bldg1 = new Building(305, 110, 30);
    private Building bldg2 = new Building(380, 125, 170);
    private Building bldg3 = new Building(245, 200, 325);
    private Building bldg4 = new Building(470, 170, 555);
    private Building bldg5 = new Building(395, 200, 755);
    private Background bg = new Background();

    public void init ()
    {
    }

    public static void main(String []args)
    {
        Skyline f = new Skyline();

        f.setSize(1017, 661); //Sets size of window
        f.setTitle("Skyline"); //Sets title of window
        f.show();
    }

    public void paintOffscreen(Graphics page)
    {
        //Draws the background
        bg.draw(page);

        //Moving square
        num++;
        if (num > 1200)
            num = 0;
        page.setColor(Color.yellow);
        page.fillRect(num,100,100,100);

        //Draws the buildings
        bldg1.draw(page);
        bldg2.draw(page);
        bldg3.draw(page);
        bldg4.draw(page);
        bldg5.draw(page);

        //Mouse move square
        int s = 100;

        page.setColor(Color.yellow);
        page.fillRect(mX - s / 2, mY - s / 2, s, s);

        repaint();
    }

    //====================================BUFFER CODE========================================
    public void paint(Graphics g)
    {
        //Clear the buffer
        Dimension d = getSize();
        checkOffscreenImage();
        Graphics offG = mImage.getGraphics();
        offG.setColor(getBackground());
        offG.fillRect(0, 0, d.width, d.height);

        //Save frame to buffer
        paintOffscreen(mImage.getGraphics());

        //Draw the buffer
        g.drawImage(mImage, 0, 0, null);

    }

    private void checkOffscreenImage()
    {
        Dimension d = getSize();

        if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height)
            mImage = createImage(d.width, d.height);
    }
    //=======================================================================================


    //==================================MOUSE MOVE         CODE======================================
    public Skyline()
    {
        addMouseMotionListener(this);
        setVisible(true);
    }

    public void mouseMoved(MouseEvent me)
    {
        Graphics g = getGraphics();
        mX = (int) me.getPoint().getX();
        mY = (int) me.getPoint().getY();
        update(g);
        //repaint();
    }

    public void mouseDragged(MouseEvent me)
    {
        mouseMoved(me);
    }
    //=======================================================================================

}

这是可能能够以某种方式修复的窗口类。

import java.applet.Applet;
import java.awt.*;
import java.util.Random;
import javax.swing.JFrame;

public class Windows extends JFrame
{
    private Random gen = new Random();
    private int height, width, locX;
    private int onOff = 0;

    public Windows()
    {
        height = 305;
        width = 110;
        locX = 30;
    }

    public Windows(int height, int width, int locX)
    {
        this.height = height;
        this.width= width;
        this.locX = locX;
    }

    public void draw(Graphics page)
    {
    page.setColor (Color.darkGray);

    page.fillRect (locX, 550 - height, width, height);

        for (int i = 550 - height + 5; i < 550; i += 15)
        {
            for (int x = locX + 5; x < locX + width; x += 15)
            {
                onOff = gen.nextInt(2);

                if(onOff == 0)
                    page.setColor(Color.black);
                else
                    page.setColor(Color.yellow);

                page.fillRect (x,i,10,10);
            }
        }
    }
}

这是建筑类以防万一。

import java.applet.Applet;
import java.awt.*;
import javax.swing.JFrame;

public class Building extends JFrame
{
    private int height, width, locX;
    private int onOff;
    private Windows windows1;// = new Windows(height, width, locX);

    public Building()
    {
        height = 305;
        width = 110;
        locX = 30;

        windows1 = new Windows(height, width, locX);
    }

    public Building(int height, int width, int locX)
    {
        this.width = width;
        this.height = height;
        this.locX = locX;

        windows1 = new Windows(height, width, locX);
    }

    public void draw(Graphics page)
    {
    page.setColor (Color.darkGray);

    page.fillRect (locX, 550 - height, width, height);

    windows1.draw(page);
    }
}

而 bg 类只是为了安全

import java.applet.Applet;
import java.awt.*;

public class Background extends Applet
{
    private int height, width;

    public Background()
    {
        height = 400;
        width = 2000;
    }

    public Background(int height, int width)
    {
        this.height = height;
        this.width = width;
    }

    public void draw(Graphics page)
    {
        //Draws the sky
        page.setColor(Color.cyan);
        page.fillRect(0,0,2000,2000);
        //Draws the grass
        page.setColor (Color.green);
        page.fillRect (0,500,width,height);
    }
}
4

1 回答 1

3

很多事情立刻在我身上跳出来......

您正在尝试使用屏幕外缓冲区,但每次在屏幕上绘制时都会重新创建它...

public void paintOffscreen(Graphics page)
{
    //Draws the background
    bg.draw(page);

    //Moving square
    num++;
    if (num > 1200)
        num = 0;
    page.setColor(Color.yellow);
    page.fillRect(num,100,100,100);

    //Draws the buildings
    bldg1.draw(page);
    bldg2.draw(page);
    bldg3.draw(page);
    bldg4.draw(page);
    bldg5.draw(page);

    //Mouse move square
    int s = 100;

    page.setColor(Color.yellow);
    page.fillRect(mX - s / 2, mY - s / 2, s, s);

    repaint();
}

此外,该方法中的最后一次调用是 to repaint。这是一个坏主意。这可能会导致您paint的方法一次又一次地被召回......

仅在需要更改时才渲染后备缓冲区会更好...

public void paint(Graphics g)
{
    super.paint(g); // YOU MUST CALL super.paint!!!!  

    //Clear the buffer
    Dimension d = getSize();
    checkOffscreenImage();

    //Draw the buffer
    g.drawImage(mImage, 0, 0, null);

}

private void checkOffscreenImage()
{
    Dimension d = getSize();

    if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height) {
        mImage = createImage(d.width, d.height);
        Graphics offG = mImage.getGraphics();
        offG.setColor(getBackground());
        offG.fillRect(0, 0, d.width, d.height);

        //Save frame to buffer
        paintOffscreen(offG);
        offG.dispose(); // If you create it, you must dispose of it...
    }
}

现在,这将引发一些使缓冲区无效的问题。这可以通过覆盖invalidate和设置mImage来实现null

public void invalidate() {  
    mImage = null;
    super.invalidate();
}

JFrame您正在从???扩展大部分组件

BuildingWindow并且Background没有自己的绘画(来自Swing的内容),您只是在调用该draw方法。无需从JFrameor扩展JApplet,它们对您的程序没有任何好处,只是混淆了问题。

您应该(很少)需要覆盖paint顶级容器,例如JFrame. 你最好使用类似的东西JPanel并覆盖该paintComponent方法,如果没有其他原因,它们(顶级容器)不是双缓冲的。

我会将 for 的逻辑移动Skyline到 a中JPanel,然后将其添加到 aJFrame中以进行显示-恕我直言

更新

我已经浏览了代码并更新了它以按照我认为应该的(基本)方式工作,并在此过程中发现了其他一些事情......

这是个坏主意……

    public void mouseMoved(MouseEvent me) {
        Graphics g = getGraphics();
        mX = (int) me.getPoint().getX();
        mY = (int) me.getPoint().getY();
        update(g);
        //repaint();
    }

您永远不需要调用update(Graphics),此外,Graphics您获得的上下文只是上次重绘的快照。这将大大减慢您的绘画过程,因为它会反复调用paint.

所以,这是我的看法...

public class Skyline extends JFrame {

    private int num = 0;
    private Building bldg1 = new Building(305, 110, 30);
    private Building bldg2 = new Building(380, 125, 170);
    private Building bldg3 = new Building(245, 200, 325);
    private Building bldg4 = new Building(470, 170, 555);
    private Building bldg5 = new Building(395, 200, 755);
    private Background bg = new Background();

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                Skyline f = new Skyline();

                f.setSize(1017, 661); //Sets size of window
                f.setTitle("Skyline"); //Sets title of window
                f.setVisible(true);
            }
        });
    }

    public Skyline() {
        setLayout(new BorderLayout());
        add(new SkyLinePane());
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public class SkyLinePane extends JPanel {

        private Image mImage; //Image buffer
        private boolean painting = false;

        private int mX, mY; //Mouse cooddinates

        public SkyLinePane() {
            addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent me) {
                    mX = (int) me.getPoint().getX();
                    mY = (int) me.getPoint().getY();
                    repaint();
                }
            });

        }

        protected void updateBuffer() {
            if (!painting && mImage == null) {
                painting = true;
                new BackgroundPainter(this).execute();
            }
        }

        //====================================BUFFER CODE========================================
        @Override
        public void paintComponent(Graphics g) {
            Dimension d = getSize();
            if (mImage != null) {
                g.drawImage(mImage, 0, 0, null);
            } else {
                updateBuffer();
            }
            g.setColor(Color.RED);
            g.drawOval(mX - 5, mY - 5, 10, 10);
        }
        //=======================================================================================

        protected void setBackground(Image image) {
            mImage = image;
            painting = false;
            repaint();
        }
    }

    public class BackgroundPainter extends SwingWorker<Image, Image> {

        private SkyLinePane skyLinePane;

        public BackgroundPainter(SkyLinePane skyLinePane) {
            this.skyLinePane = skyLinePane;
        }

        @Override
        protected Image doInBackground() throws Exception {
            Dimension d = skyLinePane.getSize();

            Image backgroundBuffer = null;
            if (d.width > 0 && d.height > 0) {

                System.out.println("Paint offscreen...");
                backgroundBuffer = createImage(d.width, d.height);
                Graphics offG = backgroundBuffer.getGraphics();
                offG.setColor(getBackground());
                offG.fillRect(0, 0, d.width, d.height);

                //Save frame to buffer
                paintOffscreen(offG);

                offG.dispose();
                System.out.println("Done Paint offscreen...");

            }

            return backgroundBuffer;
        }

        @Override
        protected void done() {
            try {
                skyLinePane.setBackground(get());
            } catch (ExecutionException exp) {
                exp.printStackTrace();
            } catch (InterruptedException exp) {
                exp.printStackTrace();
            }
        }

        public void paintOffscreen(Graphics page) {
            //Draws the background
            bg.draw(page);

            //Moving square
            num++;
            if (num > 1200) {
                num = 0;
            }
            page.setColor(Color.yellow);
            page.fillRect(num, 100, 100, 100);

            //Draws the buildings
            bldg1.draw(page);
            bldg2.draw(page);
            bldg3.draw(page);
            bldg4.draw(page);
            bldg5.draw(page);
        }
    }

    //=======================================================================================
    public class Windows {

        private Random gen = new Random();
        private int height, width, locX;
        private int onOff = 0;

        public Windows() {
            height = 305;
            width = 110;
            locX = 30;
        }

        public Windows(int height, int width, int locX) {
            this.height = height;
            this.width = width;
            this.locX = locX;
        }

        public void draw(Graphics page) {
            page.setColor(Color.darkGray);

            page.fillRect(locX, 550 - height, width, height);

            for (int i = 550 - height + 5; i < 550; i += 15) {
                for (int x = locX + 5; x < locX + width; x += 15) {
                    onOff = gen.nextInt(2);

                    if (onOff == 0) {
                        page.setColor(Color.black);
                    } else {
                        page.setColor(Color.yellow);
                    }

                    page.fillRect(x, i, 10, 10);
                }
            }
        }
    }

    public class Building {

        private int height, width, locX;
        private int onOff;
        private Windows windows1;// = new Windows(height, width, locX);

        public Building() {
            height = 305;
            width = 110;
            locX = 30;

            windows1 = new Windows(height, width, locX);
        }

        public Building(int height, int width, int locX) {
            this.width = width;
            this.height = height;
            this.locX = locX;

            windows1 = new Windows(height, width, locX);
        }

        public void draw(Graphics page) {
            page.setColor(Color.darkGray);

            page.fillRect(locX, 550 - height, width, height);

            windows1.draw(page);
        }
    }

    public class Background {

        private int height, width;

        public Background() {
            height = 400;
            width = 2000;
        }

        public Background(int height, int width) {
            this.height = height;
            this.width = width;
        }

        public void draw(Graphics page) {
            //Draws the sky
            page.setColor(Color.cyan);
            page.fillRect(0, 0, 2000, 2000);
            //Draws the grass
            page.setColor(Color.green);
            page.fillRect(0, 500, width, height);
        }
    }
}

基本上,我将天际线的核心渲染移到了它自己的面板上,并用于JComponent#paintComponent渲染天际线。

我使用 aSwingWorker将后备缓冲区的渲染卸载到另一个线程,允许 UI 在渲染后备缓冲区时保持响应。

于 2012-10-31T23:42:33.153 回答