在Java中扩展JFrame的类上绘制

在Java中扩展JFrame的类上绘制,java,swing,graphics,jframe,jpanel,Java,Swing,Graphics,Jframe,Jpanel,我对Java graphics有点陌生,我正在尝试创建一个简单的十字路口GUI界面,上面有4个交通灯,当使用我创建的以下类时-我得到一个带有一个大灰色矩形的窗口(我假设,因为我没有在中间分配交通灯,所以它已经填充了默认的灰色背景),如何控制JFrame中心的大小 这就是我想要实现的目标: 这就是我得到的: 这是JFrame类 import java.awt.*; import javax.swing.*; public class CrossroadInterface extends JF

我对Java graphics有点陌生,我正在尝试创建一个简单的十字路口GUI界面,上面有4个交通灯,当使用我创建的以下类时-我得到一个带有一个大灰色矩形的窗口(我假设,因为我没有在中间分配交通灯,所以它已经填充了默认的灰色背景),如何控制JFrame中心的大小

这就是我想要实现的目标:

这就是我得到的:

这是JFrame类

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

public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 400;
private static final int HEIGHT_OF_WINDOW = 400;

//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;

//Other
public CrossroadInterface() {
    super("My Crossroad");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
    this.setVisible(true);
    createInterface();
}

public void createInterface () {    
    tLightW = new TrafficLight();
    tLightE = new TrafficLight();
    tLightS = new TrafficLight();
    tLightN = new TrafficLight();
    this.add(tLightW, BorderLayout.WEST);
    this.add(tLightN, BorderLayout.NORTH);
    this.add(tLightE, BorderLayout.EAST);
    this.add(tLightS, BorderLayout.SOUTH);
 }
}
这是Jpanel类

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class TrafficLight extends JPanel {
    private final Color offRed = new Color(128, 0, 0);
    private final Color offGreen = new Color(0, 96, 0);
    private static final int CAR_DIAMETER = 50;
    private static final int PERSON_HEIGHT = 100;
    private static final int PERSON_WIDTH = 50;
    private int status;
    public TrafficLight() {
        super();
        this.setSize(CAR_DIAMETER, 120);
        this.setBackground(Color.BLACK);
        status = 0;
        this.setVisible(true);
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(offRed);
        g.fillOval(this.getX(), this.getY(), CAR_DIAMETER, CAR_DIAMETER);
        g.setColor(offGreen);
        g.fillOval(this.getX(), this.getY()+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
        g.setColor(offRed);
        g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
        g.setColor(offGreen);
        g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
        //drawIlluminatedLights(g);
        System.out.println(this.getX()+" "+this.getY());
    }
}
编辑: 下面的气垫船充满了鳗鱼的建议,下面是我的新课程:

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

public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 900;
private static final int HEIGHT_OF_WINDOW = 900;

//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;

//Other
public CrossroadInterface() {
    super("My Crossroad");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
    setLayout(new GridLayout(3,3));
    createInterface();
}

public void createInterface () {    
    tLightW = new TrafficLight();
    tLightE = new TrafficLight();
    tLightS = new TrafficLight();
    tLightN = new TrafficLight();
    this.add(new JPanel());
    this.add(tLightW);
    this.add(new JPanel());
    this.add(tLightN);
    this.add(new JPanel());
    this.add(tLightE);
    this.add(new JPanel());
    this.add(tLightS);
    this.setVisible(true);
 }
}

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JPanel;

public class TrafficLight extends JPanel {
    private final Color offRed = new Color(128, 0, 0);
    private final Color offGreen = new Color(0, 96, 0);
    private static final int CAR_DIAMETER = 50;
    private static final int PERSON_HEIGHT = 50;
    private static final int PERSON_WIDTH = 50;
    private int status;
    public TrafficLight() {
        super();
        status = 0;
        this.setPreferredSize(new Dimension(CAR_DIAMETER,2*CAR_DIAMETER+2*PERSON_HEIGHT));
        this.setVisible(true);
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(offRed);
        g.fillOval(100, 50, CAR_DIAMETER, CAR_DIAMETER);
        g.setColor(offGreen);
        g.fillOval(100, 50+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
        g.setColor(offRed);
        g.fillRect(100, 50+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
        g.setColor(offGreen);
        g.fillRect(100, 50+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
        //drawIlluminatedLights(g);
    }
}

问题不在于JFrame的中心太大,而是因为周围的JPanel太小。了解大多数Swing布局管理器都尊重组件的首选大小,并使用此设置组件的大小。你的其他问题包括

  • 使用
    getX()
    getY()
    放置图形。这些值给出了JPanel在其容器中的位置,但这无助于放置图形,因为在JPanel中绘制图形时,图形的位置是相对于JPanel中像素的位置而不是其容器放置的,因此使用这些方法会让您陷入困境
  • 在添加所有组件之前调用JFrame的
    setVisible(true)
    。这可能会导致无法显示所有组件
  • 让您的TrafficLight类扩展JPanel。您最好使用一个JPanel来完成所有绘图,并且让TrafficLight类不从任何Swing组件扩展,而是成为一个逻辑类。给它一个
    public void draw(Graphics2D g2)
    方法,您可以在绘图JPanel的paintComponent方法中调用该方法
例如:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.swing.*;

@SuppressWarnings("serial")
public class CrossRoads2 extends JPanel {
    private static final int PREF_W = 400;
    private static final int PREF_H = PREF_W;
    private static final int TIMER_DELAY = 100;
    List<TrafficLight2> lights = new ArrayList<>();

    public CrossRoads2() {
        // create a timer to randomly change traffic light state
        // and start it
        new Timer(TIMER_DELAY, new TimerListener()).start();

        // create 4 TrafficLight2 objects and place them at 4
        // compass locations, and add to lights ArrayList
        int x = (PREF_W - TrafficLight2.getWidth()) / 2;
        int y = 0;
        lights.add(new TrafficLight2(x, y));
        x = 0;
        y = (PREF_H - TrafficLight2.getHeight()) / 2;
        lights.add(new TrafficLight2(x, y));
        x = (PREF_W - TrafficLight2.getWidth());
        lights.add(new TrafficLight2(x, y));
        x = (PREF_W - TrafficLight2.getWidth()) / 2;
        y = (PREF_H - TrafficLight2.getHeight());
        lights.add(new TrafficLight2(x, y));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        // cast g into a Graphics2 object
        Graphics2D g2 = (Graphics2D) g;

        // for smooth rendering
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // iterate through the ArrayList, calling the draw method on each light 
        for (TrafficLight2 light : lights) {
            light.draw(g2);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        // give our JPanel a decent size
        return new Dimension(PREF_W, PREF_H);
    }

    // ActionListener that randomly changes the LightState of each traffic light
    private class TimerListener implements ActionListener {
        private Random random = new Random();

        @Override
        public void actionPerformed(ActionEvent e) {
            for (TrafficLight2 light : lights) {
                // random number 0 to 2
                int randomIndex = random.nextInt(LightState.values().length);

                // get one of the LightStates using the index above
                LightState lightState = LightState.values()[randomIndex];
                // set our light to this state
                light.setLightState(lightState);
            }
            repaint();
        }
    }

    private static void createAndShowGui() {
        CrossRoads2 mainPanel = new CrossRoads2();

        JFrame frame = new JFrame("Cross Roads");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}

这:
This.getX()
This.getY()
不是您想要做的,因为在JPanel中绘制时,重要的是同一JPanel中的相对位置,而不是JPanel本身的位置。摆脱那些电话,使用合理的contants。还要为您的JPanels提供合理的PreferredSize。在添加所有组件之后,而不是之前,还要在JFrame上调用
setVisible(true)
。请注意,如果这是我的项目,我会以不同的结构对其进行构造。我只使用一个JPanel来绘制,我的单个TrafficLight对象不会从一个Swing组件(如JPanel)扩展,而是逻辑组件,可以在单个JPanel中绘制。感谢您的回答,我做了一些更改,现在看起来就像我希望的那样。关于你的第三个注意事项,你能给我举一个类似的例子吗?我的变量应该是什么?我将如何实现draw方法?@EddieRomanenco:我已经给出了一个例子来说明一种方法。它可能不会完全适用于您,但这不是本示例的目的。更重要的是向您展示一个通用的示例。运行代码并查看。非常感谢,非常感谢您的帮助。我试图添加两个较低的矩形(将用作行人专用道的交通灯),但GUI仅显示添加的最后两个灯,您的代码中有什么阻止显示所有4个灯?@Eddieromaneco:我不知道,但不要复制我的代码。相反,从中获取想法,然后写下自己的想法。这样做会更好,并且通过这样做,您将确切地了解代码在做什么。
class TrafficLight2 {
    private static final int ELLIPSE_W = 40;
    private static final int GAP = 4;
    private int x;
    private int y;
    private LightState lightState = LightState.RED; // what color is bright

    // map to hold our 3 ellipses, each one corresponding to a LightState
    private Map<LightState, Shape> lightMap = new EnumMap<>(LightState.class);

    public TrafficLight2(int x, int y) {
        // create 3 ellipses, one each for RED, YELLOW, GREEN
        // place each one below the previous
        // associate each one with one of our RED, YELLOW, or GREEN LightStates
        // putting the Ellipse into the map with the light state as key
        this.x = x;
        this.y = y;
        int tempX = x + GAP;
        int tempY = y + GAP;
        lightMap.put(LightState.RED, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
        tempY += ELLIPSE_W + GAP;
        lightMap.put(LightState.YELLOW, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
        tempY += ELLIPSE_W + GAP;
        lightMap.put(LightState.GREEN, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
    }

    // called by JPanel's paintComponent
    public void draw(Graphics2D g2) {
        // iterate through the 3 LightStates
        for (LightState ltSt : LightState.values()) {
            // if the ltSt in the for loop is this traffic light's LightState
            // then the display color should be bright
            Color c = ltSt == lightState ? ltSt.getColor() : 
                // other wise the display color should be very dark
                ltSt.getColor().darker().darker().darker();
            g2.setColor(c);
            g2.fill(lightMap.get(ltSt)); // fill the oval with color
            g2.setColor(Color.BLACK);
            g2.draw(lightMap.get(ltSt));  // draw a black border
        }
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public LightState getLightState() {
        return lightState;
    }

    public void setLightState(LightState lightState) {
        this.lightState = lightState;
    }

    // static method for the width of our traffic lights
    public static int getWidth() {
        return 2 * GAP + ELLIPSE_W;
    }

    // static method for the height of our traffic lights
    public static int getHeight() {
        return 4 * GAP + 3 * ELLIPSE_W;
    }
}
// enum that encapsulates the 3 possible states of the traffic light
enum LightState {
    RED("Red", Color.RED), YELLOW("Yellow", Color.YELLOW), GREEN("Green", Color.GREEN);

    private LightState(String text, Color color) {
        this.text = text;
        this.color = color;
    }

    private String text;
    private Color color;

    public String getText() {
        return text;
    }
    public Color getColor() {
        return color;
    }    
}