有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java阻止某些图形重新绘制

我完全陷入了这个程序的一个问题,我必须用swing画一个城市。基本上,我想做的是使它的窗口不会改变每一帧。我已经尝试了我所能想到的一切,但没有任何效果

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

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);
    }
}

共 (1) 个答案

  1. # 1 楼答案

    很多事情立刻向我袭来

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

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

    此外,该方法中的最后一个调用是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扩展大部分组件

    BuildingWindowBackground没有自己的绘画(从Swing的内容来看),您只是调用draw方法。没有必要从JFrameJApplet扩展,它们没有给您的程序带来任何好处,只是混淆了问题

    在像JFrame这样的顶级容器上,您应该很少需要重写paint。如果没有其他原因,它们(顶级容器)不是双缓冲的,那么最好使用JPanel之类的方法并重写paintComponent方法

    我会将Skyline的逻辑移到JPanel中,然后将其添加到JFrame中以显示-IMHO

    更新

    我已经阅读了代码,并按照我认为应该的(基本)方式对其进行了更新,同时还发现了其他一些东西

    这是个坏主意

        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来渲染天际线

    我使用了一个SwingWorker将备份缓冲区的呈现卸载到另一个线程,允许UI在呈现备份缓冲区时保持响应