【问题标题】:How to keep drawn shapes from disappearing in Java?如何防止绘制的形状在 Java 中消失?
【发布时间】:2013-10-10 00:13:25
【问题描述】:

我正在做一个项目来创建一个可以绘制随机形状的屏幕保护程序。我有几个问题,但我现在主要关心的是如何让形状留在屏幕上,而不是在创建后消失。这是我的代码。我不能使用任何循环,也不想改变我所拥有的任何功能(除了可能的 shapeDrawn)。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ScreenSaver2 extends JPanel implements ActionListener {
    private JFrame frame = new JFrame("FullSize");
    private Rectangle rectangle;
    boolean full;


    protected void paintComponent(Graphics g) {
        int r = (int)(Math.random() * 255);
        int gr = (int)(Math.random() * 255);
        int b = (int)(Math.random() * 255);
        Color color = new Color(r, gr, b);
        int width = 10 + (int)(Math.random() * 40);
        int height = 10 + (int)(Math.random() * 40);
        int x = (int)(Math.random() * (getWidth() - width));
        int y = (int)(Math.random() * (getHeight() - height));
        int whichShape = (int)(Math.random() * 3);
        int shapesDrawn = 0;

        super.paintComponent(g);
        if (shapesDrawn >= 30) {
            shapesDrawn = 0;
        }

        switch (whichShape) {
        case 0:
            g.setColor(color);
            g.drawLine(x, y, x + width, y + height);
            shapesDrawn++;
            break;
        case 1:
            g.setColor(color);
            g.drawRect(x, y, width, height);
            shapesDrawn++;
            break;
        case 2:
            g.setColor(color);
            g.drawRoundRect(x, y, width, height, 25, 25);
            shapesDrawn++;
            break;
        case 3:
            g.setColor(color);
            g.drawOval(x, y, width, height);
            shapesDrawn++;
            break;
        }

    }


    ScreenSaver2() {
        // Remove the title bar, min, max, close stuff
        frame.setUndecorated(true);
        // Add a Key Listener to the frame
        frame.addKeyListener(new KeyHandler());
        // Add this panel object to the frame
        frame.add(this);
        // Get the dimensions of the screen
        rectangle = GraphicsEnvironment.getLocalGraphicsEnvironment()
        .getDefaultScreenDevice().getDefaultConfiguration().getBounds();
        // Set the size of the frame to the size of the screen
        frame.setSize(rectangle.width, rectangle.height);
        frame.setVisible(true);
        // Remember that we are currently at full size
        full = true;
        // set and initialize timer
        Timer t = new Timer(500, this);
        t.setDelay(500);
        t.start();

    }

    // This method will run when any key is pressed in the window
    class KeyHandler extends KeyAdapter {
        public void keyPressed(KeyEvent e) {
            // Terminate the program.
            if (e.getKeyChar() == 'x') {
                System.out.println("Exiting");
                System.exit(0);
            }
            // Change background color
            else if (e.getKeyChar() == 'r') {
                System.out.println("Change background color");
                setBackground(new Color((int)(Math.random() * 256), (int)(Math.random() * 256), (int)(Math.random() * 256)));
                repaint();
            }
            // Resize to half-screen
            else if (e.getKeyChar() == 'z') {
                System.out.println("Resizing");
                frame.setSize((int)rectangle.getWidth() / 2, (int)rectangle.getHeight());
            }
        }
    }

    public void actionPerformed(ActionEvent e) {
        repaint();
    }

    public static void main(String[] args) {
        ScreenSaver2 obj = new ScreenSaver2();
    }
}

【问题讨论】:

  • 为什么不能使用循环?
  • @nhgrif 这是对愚蠢分配的限制:P
  • 您不断地调用具有随机形状的 repaint(),但您想保留旧油漆。 ~^
  • 类似 public void actionPerformed(ActionEvent e) { if (shapesDrawn >= 30) { shapeDrawn = 0;重绘(); }
  • @Ryel,FWIW,您应该让您的教授看看Custom Painting Approaches,了解执行此类操作的正常方法。这些示例应修改为具有“addShape()”方法,该方法可以将形状添加到 ArrayList 或在 BufferedImage 上绘制形状。然后你的 Timer 会调用 addShape() 方法并添加一个新的形状。

标签: java swing drawing jpanel paintcomponent


【解决方案1】:

不要将paintComponent 中的每个新形状直接绘制到Graphics 上下文中,而是使用后备缓冲区先将形状渲染到Graphics 上下文中。

这样,您只需在每次呈现新形状时维护一个简单的计数器。

这也很麻烦,因为paintComponent 可能由于多种原因被调用,而您无法控制很多原因,这意味着您的程序可以在任何计时器滴答实际发生之前绘制更多形状...

更新示例

您剩下的唯一选择是创建一个后备缓冲区,您可以根据需要在其上绘制每个更改。这将“存储”油漆周期之间的每个变化。然后,您可以简单地将其绘制到屏幕上...

private BufferedImage img;

//...

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    // Create the backing buffer
    // This is a little cheat, creating a new image when the number of shapes
    // exceeds the requirements, but it saves messing about with clearing
    // a alpha image ;)
    if (img == null || shapesDrawn >= 30) {
        img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
        shapesDrawn = 0;
    } else if (img.getWidth() != getWidth() || img.getHeight() != img.getHeight()) {
        // Update the backing buffer to meet the requirements of the changed screen size...
        BufferedImage buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D gbuffer = buffer.createGraphics();
        gbuffer.drawImage(img, 0, 0, this);
        gbuffer.dispose();
        img = buffer;
    }

    // Get a reference to the backing buffers graphics context...
    Graphics2D gbuffer = img.createGraphics();

    // Paint the shapes to the backing buffer...
    int r = (int) (Math.random() * 255);
    int gr = (int) (Math.random() * 255);
    int b = (int) (Math.random() * 255);
    Color color = new Color(r, gr, b);
    int width = 10 + (int) (Math.random() * 40);
    int height = 10 + (int) (Math.random() * 40);
    int x = (int) (Math.random() * (getWidth() - width));
    int y = (int) (Math.random() * (getHeight() - height));
    int whichShape = (int) (Math.random() * 3);
    int shapesDrawn = 0;

    switch (whichShape) {
        case 0:
            gbuffer.setColor(color);
            gbuffer.drawLine(x, y, x + width, y + height);
            shapesDrawn++;
            break;
        case 1:
            gbuffer.setColor(color);
            gbuffer.drawRect(x, y, width, height);
            shapesDrawn++;
            break;
        case 2:
            gbuffer.setColor(color);
            gbuffer.drawRoundRect(x, y, width, height, 25, 25);
            shapesDrawn++;
            break;
        case 3:
            gbuffer.setColor(color);
            gbuffer.drawOval(x, y, width, height);
            shapesDrawn++;
            break;
    }
    // Dispose of the buffers graphics context, this frees up memory for us
    gbuffer.dispose();
    // Paint the image to the screen...
    g2d.drawImage(img, 0, 0, this);
    g2d.dispose();
}

可能是我曾经给过的最糟糕的建议

改变...

super.paintComponent(g);
if (shapesDrawn >= 30) {
    shapesDrawn = 0;
}

到...

if (shapesDrawn >= 30) {
    super.paintComponent(g);
    shapesDrawn = 0;
}

【讨论】:

  • 我今天早上和我的教授谈过,他特别说要把所有东西都移到paintComponent中。它正在工作,但形状不会留在屏幕上,这意味着我不知道计数器是否在工作。
  • 还有@MadProgrammer,这很令人沮丧,因为我基本上不得不把你帮助我的所有东西都扔掉,哈哈
  • 每次调用paintComponent 都会清除图形上下文,这是预期的功能。如果没有循环,您唯一的选择是先将输出绘制到后备缓冲区,然后再将其绘制到屏幕上......
  • 哦,你的意思是画出形状,给它们编号,然后初始化开关组件?
  • @Ryel 这可能是我听过的最愚蠢的建议。 Graphics 上下文是一个共享资源,这意味着在组件之前绘制的任何内容仍将在其上“可见”,这就是调用 super.paintComponent 的意义所在。我不在乎屏幕上是否没有其他内容,这是在鼓励不良做法,这些做法会比你眨眼的速度更快地转过身来咬你的后端 - 从与挥杆专业人士一起编码超过 14 年的人那里得到它年;)
猜你喜欢
  • 1970-01-01
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 2012-07-09
  • 2012-12-09
  • 1970-01-01
相关资源
最近更新 更多