在JFrame中不能绘制一个以上的正方形

时间:2018-12-27 22:34:33

标签: java swing jframe paint

无法使程序打印多于一个正方形。


我现在的代码

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

public class MyApplication extends JFrame {

    private static final Dimension WindowSize = new Dimension(600, 600);
    private int xCord=9, yCord=32, width=80, height=80;

    public MyApplication() {
        //Create and set up the window
        this.setTitle("Squares");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Display the window centered on the screen
        Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        int x = screensize.width / 2 - WindowSize.width / 2;
        int y = screensize.height / 2 - WindowSize.height / 2;
        setBounds(x, y, WindowSize.width, WindowSize.height);
        setVisible(true);
    }

    public static void main(String args[]) {
        MyApplication window = new MyApplication();
    }

    public void paint(Graphics g) {
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        g.setColor(Color.getHSBColor(red, green, blue));
        g.fillRect(xCord, yCord, width, height);

        while((yCord+height)<600){
            if((xCord+width)>600){
                xCord=9;
                yCord+=80;
            }
            else xCord+=80;
            repaint();
        }
    }
}

我正在尝试用不同颜色的正方形填充600x600的窗口,一旦行已满,这些颜色将转到新行。

2 个答案:

答案 0 :(得分:3)

首先,不要。

请勿覆盖paint之类的顶级容器中的JFrame

JFrame是一个复合组件,这意味着它们是其表面和用户之间的许多层,并且由于绘画系统的工作方式,可以独立于框架进行绘画,从而产生结果很奇怪。

A frames layers

顶级容器没有双重缓冲,这意味着您的更新将闪烁。

除非您完全确定自己知道自己在做什么,否则请调用paint方法的超级方法。

首先查看Performing Custom PaintingPainting in AWT and Swing,以了解有关绘画在Swing中的工作方式以及如何使用它的更多详细信息。

这个...

Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - WindowSize.width / 2;
int y = screensize.height / 2 - WindowSize.height / 2;
setBounds(x, y, WindowSize.width, WindowSize.height);

在许多层面上都是个坏主意。

Toolkit#getScreenSize没有考虑其他UI元素的大小,这会减少屏幕上可用的可视区域,例如某些OS上的任务栏/停靠栏或菜单栏。

在基于窗口的类上使用setBounds(x, y, WindowSize.width, WindowSize.height);也是一个坏主意,因为可用的可见区域是窗口大小减去窗口装饰的大小,这意味着实际可见的区域要比您指定的要小,因为直接在框架上绘画,则有在框架装饰下绘画的风险。

您可以查看How can I set in the midst?了解更多详细信息

您现在应该进行的涂装一件事是,涂装具有破坏性,也就是说,每次发生涂装周期时,都希望您完全重涂组件的当前状态。

当前,这个...

public void paint(Graphics g) {
    int red = (int) (Math.random() * 255);
    int green = (int) (Math.random() * 255);
    int blue = (int) (Math.random() * 255);
    g.setColor(Color.getHSBColor(red, green, blue));
    g.fillRect(xCord, yCord, width, height);

    while ((yCord + height) < 600) {
        if ((xCord + width) > 600) {
            xCord = 9;
            yCord += 80;
        } else {
            xCord += 80;
        }
        repaint();
    }
}

将仅根据xCord方法退出后yCordpaint的最后一个值绘制一个矩形。

Swing使用被动渲染引擎,这意味着系统将确定要绘画的内容以及何时不受控制。您可以通过使用repaint向系统发出“请求”,但这取决于系统来决定何时以及将要绘画什么,这意味着可以将多个请求优化到单个绘画遍。 / p>

此外,绘画只不过要绘画当前状态而已。应该避免直接或间接更改状态,尤其是如果该更改触发了新的绘制过程,因为这会突然将程序的性能降低到0,从而使其失效。

那答案是什么?

好吧,改变一切...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class MyApplication {

    public static void main(String[] args) {
        new MyApplication();
    }

    public MyApplication() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class TestPane extends JPanel {

        private static final Dimension DESIRED_SIZE = new Dimension(600, 600);
        private int width = 80, height = 80;

        public TestPane() {
        }

        @Override
        public Dimension getPreferredSize() {
            return DESIRED_SIZE;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();

            int xCord = 0, yCord = 0;

            while ((yCord) < getHeight()) {
                int red = (int) (Math.random() * 255);
                int green = (int) (Math.random() * 255);
                int blue = (int) (Math.random() * 255);
                g2d.setColor(Color.getHSBColor(red, green, blue));
                g2d.fillRect(xCord, yCord, width, height);
                if ((xCord + width) > getWidth()) {
                    xCord = 0;
                    yCord += 80;
                } else {
                    xCord += 80;
                }
            }
            g2d.dispose();
        }

    }

}

Lots of squares

崩溃...

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);

这将创建一个Jframe的实例,您真的不想从JFrame进行扩展,也没有在该类中添加任何新功能

frame.pack()将窗口包装在内容周围,以确保框架始终比所需的内容尺寸大(按框架装饰的数量)

frame.setLocationRelativeTo(null);将以系统独立的方式使窗口居中。

下一步...

private static final Dimension DESIRED_SIZE = new Dimension(600, 600);
private int width = 80, height = 80;

public TestPane() {
}

@Override
public Dimension getPreferredSize() {
    return DESIRED_SIZE;
}

我已经使用DESIRED_SIZE向父容器布局管理器提供了大小调整提示。

最后...

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();

    int xCord = 0, yCord = 0;
    while ((yCord) < getHeight()) {
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        g2d.setColor(Color.getHSBColor(red, green, blue));
        g2d.fillRect(xCord, yCord, width, height);
        if ((xCord + width) > getWidth()) {
            xCord = 0;
            yCord += 80;
        } else {
            xCord += 80;
        }
    }
    g2d.dispose();
}

请注意,在这里,我已经将xCordyCord的位置更改为零,我不再需要猜测框架的装饰。除了创建局部变量之外,以便在再次调用该方法时,这些值都将重置为零。

您没有“必须”将Graphics引用强制转换为Graphics2D,但是Graphics2D是更强大的API。我也想复制它的状态,但这就是我,您的代码很简单,因此不太可能对组件后可能绘制的任何其他内容产生不利影响。

还要注意,我使用了getWidthgetHeight而不是“幻数”,这意味着您可以调整窗口的大小,并且绘画会适应。

答案 1 :(得分:0)

您可以尝试将整个绘制机制放在循环中,以在一次调用中完成。因此,您将不需要在paint方法本身内部调用repaint:

public void paint(Graphics g) {

    while((yCord+height)<600){

        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        g.setColor(Color.getHSBColor(red, green, blue));
        g.fillRect(xCord, yCord, width, height);

        if((xCord+width)>600){
            xCord=9;
            yCord+=80;
        }
        else xCord+=80;
    }
}