简体   繁体   中英

Painting on a class extending JFrame in Java

i'm kind of new with Java graphics, i'm trying to create a simple crossroad GUI interface with 4 traffic lights on it, when using the following classes that I have created - I get a window with a large grey rectangle on it (I assume that since I didn't allocate a traffic lights in the center it has been filled with the default grey background), how do I control the size of the center of the JFrame?

This is what i'm looking to acheive: 在此输入图像描述

This is what i'm getting: 在此输入图像描述

This is the JFrame class.

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

This is the Jpanel class.

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

EDIT: Following Hovercraft Full Of Eels' advise, here are my new classes:

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

在此输入图像描述

Your problem is not that the center of the JFrame is too large, but rather it's because the size of your surrounding JPanels are too small. Understand that most Swing layout managers respect a components preferred size, and use this to set the size of the component. Your other problems include

  • using getX() and getY() to place your drawings. These values give the location of the JPanel within its container, but that won't help you place your drawing since when you draw within the JPanel the drawing's location is placed relative to the location of the pixel within the JPanel not its container, so using these methods will mess you up.
  • Calling the JFrame's setVisible(true) before adding all components. This risks not displaying all components.
  • Making your TrafficLight class extend JPanel. You're far better off using a single JPanel to do all the drawing and have your TrafficLight class not extend from any Swing component but rather be a logical class. Give it a public void draw(Graphics2D g2) method that you can call within the drawing JPanel's paintComponent method.

For example:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM