【问题标题】:Stop some graphics from redrawing阻止某些图形重绘
【发布时间】:2012-10-31 23:19:27
【问题描述】:

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

这是绘制所有内容的主类

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

【问题讨论】:

    标签: java swing graphics paint repaint


    【解决方案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 扩展,它们不会为您的程序增加任何好处,只会混淆问题。

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

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

    更新

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

    这是个坏主意……

        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 在后台缓冲区渲染时保持响应。

    【讨论】:

    • 非常感谢您的回复!我一定会试试你所说的。我认为这里和那里会有一些糟糕的编程,因为我对 Java 还很陌生,但我现在应该能够修复它。再次感谢!
    • 所以我有机会尝试这段代码,它可以编译,但不能正常工作。除非我去掉 checkoffcreenimage 中的 if 语句,否则它不会更新。当我删除它时,它运行缓慢,我遇到和以前一样的问题。
    • 非常感谢!肯定需要一些时间来整理代码以获得我想要的东西,但这是一个很棒的起点。非常感谢 MadProgrammer。
    猜你喜欢
    • 1970-01-01
    • 2017-07-19
    • 2012-10-22
    • 2012-05-31
    • 1970-01-01
    • 2022-07-28
    • 1970-01-01
    • 2018-06-11
    • 1970-01-01
    相关资源
    最近更新 更多