Drawing on top of multiple JButtons - java

So, I have multiple objects of the class Square, which is the subclass of JButton. I have an instance of the class Board, which contains a few instances of Square. What I want to do is when I press one of the buttons (squares), draw a shape (a circle) on top of it. For doing that, I have a boolean variable in the Square class, namely isClicked, that basically decides what has to be drawn in the paintComponent method.
The problem is that buttons start to behave in a weird way when I have a few of them. Surprisingly, if there is only one of them, there is no problem at all. At first, I had thought the problem might be related to threads, however, I put the main code into invokeLater method and that did not help at all.
I saw a solution using BufferedImage, but I would like to see if there is any possibility to solve the problem doing it my way.
Sorry for possibly not perfect English.
Square class:
public class Square extends JButton implements ActionListener {
private int number;
private boolean isClicked;
public Square(int x) {
number = x;
isClicked = false;
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
if (!isClicked) {
super.paintComponent(g);
} else {
System.out.println("EXECUTED for: " + number);
g2d.drawOval(this.getX(), this.getY(), 100, 100);
}
}
#Override
public void actionPerformed(ActionEvent e) {
isClicked = !isClicked;
System.out.println(isClicked + " " + number);
repaint();
}
}
Board class:
public class Board extends JPanel {
private static final int BOARD_WIDTH = (int) (TicTacToe.WIDTH * 0.7);
private static final int VERTICAL_LINE_LENGTH = (int) (TicTacToe.WIDTH * 0.5);
private static final int HORIZONTAL_LINE_LENGTH = (int) (TicTacToe.HEIGHT * 0.8);
private static final int STROKE_WIDTH = 5;
private Square[] squares;
public Board() {
}
public void addButtons() {
squares = new Square[9];
for (int i = 0; i < 3; i++) {
Square square = new Square(i);
square.setPreferredSize(new Dimension(30, 30));
square.addActionListener(square);
this.add(square);
squares[i] = square;
((GridLayout)this.getLayout()).setHgap(30);
((GridLayout)this.getLayout()).setVgap(30);
}
}
public Square[] getButtons() {
return squares;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(STROKE_WIDTH));
// Horiztontal lines
g2d.drawLine(0, TicTacToe.HEIGHT / 3,
BOARD_WIDTH, TicTacToe.HEIGHT / 3);
g2d.drawLine(0, 2 * TicTacToe.HEIGHT / 3,
BOARD_WIDTH, 2 * TicTacToe.HEIGHT / 3);
// Vertical lines
g2d.drawLine(BOARD_WIDTH / 3, 0, BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
g2d.drawLine(2 * BOARD_WIDTH / 3, 0, 2 * BOARD_WIDTH / 3,
TicTacToe.HEIGHT);
}
}
Main method:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Board board = new Board();
board.setPreferredSize(new Dimension((int) (WIDTH * 0.7), HEIGHT));
board.setLayout(new GridLayout(3, 3));
board.addButtons();
GameOptions opt = new GameOptions();
opt.setPreferredSize(new Dimension((int) (WIDTH * 0.3), HEIGHT));
JFrame frame = new JFrame("Tic Tac Toe");
frame.setLayout(new FlowLayout());
frame.add(board);
frame.add(opt);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});

Your use of getX() and getY() on the button's drawing code is completely wrong and does not belong. These methods return the location of the button relative to its container, and so while this might work for a button located at the upper left, it will fail for anything else since you'll end up drawing somewhere far away from the button itself, and so many of your drawings will never show.
You'd be much better off not extending JButton but instead simply swapping ImageIcons that display what you want drawn on the JButton. This is much simpler and much more idiot-proof. You set the button's icon by calling .setIcon(myImageIcon) on it, passing in the icon of choice.
But if you absolutely wanted to draw on the button, you'd do so without using getX() or getY(). You'd also probably want to use a JToggleButton as the parent class, since you're toggling state. For example, my MCVE:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawButtonPanel extends JPanel {
private static final int SIDE = 3;
private static final int GAP = 5;
private static final Color BG = Color.BLACK;
public DrawButtonPanel() {
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new GridLayout(SIDE, SIDE, GAP, GAP));
setBackground(BG);
for (int i = 0; i < SIDE * SIDE; i++) {
// add(new DrawButton1());
DrawButton2 drawButton2 = new DrawButton2(i);
AbstractButton button = drawButton2.getButton();
add(button);
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new DrawButtonPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DrawButton2 {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
private AbstractButton button = new JToggleButton();
private int index;
public DrawButton2(int index) {
this.index = index;
button.setBorderPainted(false);
button.setBorder(null);
button.setIcon(createPlainIcon());
button.setSelectedIcon(createSelectedIcon());
button.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println("Index: " + index);
}
}
});
}
public int getIndex() {
return index;
}
private Icon createPlainIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(new Color(0, 0, 0, 0));
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.dispose();
return new ImageIcon(img);
}
private Icon createSelectedIcon() {
BufferedImage img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
g2d.setColor(BG);
g2d.fillRect(0, 0, PREF_W, PREF_H);
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, PREF_W - 2 * GAP, PREF_H - 2 * GAP);
g2d.dispose();
return new ImageIcon(img);
}
public AbstractButton getButton() {
return button;
}
}
#SuppressWarnings("serial")
class DrawButton1 extends JToggleButton {
private static final int PREF_W = 200;
private static final int PREF_H = PREF_W;
private static final int GAP = 20;
private static final float STROKE_WIDTH = 15f;
private static final Stroke BASIC_STROKE = new BasicStroke(STROKE_WIDTH);
private static final Color COLOR = Color.RED;
private static final Color BG = Color.LIGHT_GRAY;
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(BASIC_STROKE);
if (!isSelected()) {
super.paintComponent(g);
} else {
g2d.setColor(BG);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(COLOR);
g2d.drawOval(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
}
g2d.dispose(); // since we created a new one
}
}
Edited to show how to do this with JToggleButton and icons.

Related

Is there a way to make a 2nd click change the result?

I'm trying to make it when you click the cameraButton, the graphics show, but when clicked again, it closes.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int camButtonWidth = 500;
int camButtonHeight = 33;
int camButtonX = (width - camButtonWidth) / 2;
int camButtonY = (height - camButtonHeight) - 5;
int camWidth = width - 50;
int camHeight = (height - (camButtonHeight * 2)) - 10;
int camX = (width - camWidth) / 2;
int camY = ((height - camHeight) - camButtonHeight) / 2;
Graphics2D g1 = (Graphics2D) g;
Graphics2D g2 = (Graphics2D) g;
Graphics2D g3 = (Graphics2D) g;
RoundRectangle2D camButton = new RoundRectangle2D.Double(camButtonX, camButtonY, camButtonWidth, camButtonHeight, 25, 25);
RoundRectangle2D cameras = new RoundRectangle2D.Double(camX, camY, camWidth, camHeight, 25, 25);
// Background
g1.setColor(Color.BLACK);
g1.fillRect(0, 0, width, height);
addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if (camButton.contains(e.getPoint())) {
camUp = !camUp;
repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
// Camera Button
g2.setColor(camColor);
g2.fill(camButton);
paintCameras = camUp;
// Cameras
g3.setColor(camColor);
if (paintCameras) {
g3.fill(cameras);
}
repaint();
}
Try to change make it when you click the camera button, a graphics object shows, but when clicked again, it closes.
To get this sort of program to work you should:
Create your MouseListener in code that is only called once, such as within a constructor
Create an instance field in the class to represent the camera button, such as a Rectangle or RoundRectangle2D and give it a viable object reference
In the mouse listener, toggle the state of a boolean variable if a click occurs within the shape that represents the camera button, e.g., camUp = !camUp; as you're doing
And then call repaint().
In the paintComponent method, check the state of the boolearn variable with an if statement, and if true, draw the image inside the if statement.
Keep the mouse listener and the painting code separate and in separate methods (or constructor).
Never call repaint() within a painting method as that will cause an uncontrolled animation. If you need a Swing animation, then use a Swing Timer so that you can fully control it. I don't see the need for it here.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class GraphicsExample extends JPanel {
private static final int IMG_WIDTH = 400;
private static final int PREF_W = (3 * IMG_WIDTH) / 2;
private static final int PREF_H = PREF_W;
private static final Color BTN_COLOR = Color.RED;
private static final Color HOVER_COLOR = new Color(255, 100, 100);
private static final Color BTN_CLK_COLOR = new Color(180, 0, 0);
private static final int IMG_X = IMG_WIDTH / 2;
private static final int IMG_Y = IMG_X;
private double camX = 10;
private double camY = camX;
private double camWidth = 200;
private double camHeight = 80;
private Color buttonColor = Color.RED;
private RoundRectangle2D cameraButton = new RoundRectangle2D.Double(camX, camY, camWidth, camHeight, 25, 25);
private Image img;
private boolean showImage = false;
private JCheckBox toggleModeChkBox = new JCheckBox("Toggle Mode");
// private boolean toggleMode = true;
public GraphicsExample() {
add(toggleModeChkBox);
setPreferredSize(new Dimension(PREF_W, PREF_H));
img = createMyImage();
MouseAdapt mouseAdapt = new MouseAdapt();
addMouseListener(mouseAdapt);
addMouseMotionListener(mouseAdapt);
}
private Image createMyImage() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setPaint(new GradientPaint(0, 0, Color.RED, 100, 100, Color.BLUE, true));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int gap = 10;
g2.fillOval(gap, gap, IMG_WIDTH - 2 * gap, IMG_WIDTH - 2 * gap);
g2.dispose();
return img;
}
private class MouseAdapt extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = BTN_CLK_COLOR;
if (toggleModeChkBox.isSelected()) {
showImage = !showImage;
} else {
showImage = true;
}
} else {
buttonColor = BTN_COLOR;
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = HOVER_COLOR;
} else {
buttonColor = Color.RED;
}
if (!toggleModeChkBox.isSelected()) {
showImage = false;
}
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = HOVER_COLOR;
} else {
buttonColor = Color.RED;
}
if (!toggleModeChkBox.isSelected()) {
showImage = false;
}
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(buttonColor);
g2.fill(cameraButton);
if (showImage) {
int x = (getWidth() - IMG_WIDTH) / 2;
int y = (getHeight() - IMG_WIDTH) / 2;
g2.drawImage(img, x, y, this);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
GraphicsExample mainPanel = new GraphicsExample();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

Need help to adjust a Zoom system in Graphics2D - Java

This is my 1st question here. I'm trying to build a White Page adjustable by zoom. It's inside a JScrollPane, so the size of the JScrollPane's ScrollBars are adjustable in the Dimension of that JPanel.
I want to adjust the size of those ScrollBars as the Size of the page (variables width and height in the code) + 2 borderSize, so the full size is equal the Page + margin of a borderSize around it. It works if zoom = 1.0.
If zoom < 1.0, the scroll bar is smaller than the Page and cut a piece of it. If zoom > 1 the Dimension size is way bigger than the page, leaving a huger border on its right and down corners, bigger than the borderSize.
How do I do this?
PS: I'm started learning java by myself, in the Quarantine last year, never had a teacher, just the internet, so any critics or suggestions, please, tell me.
Here's the JPanel's code:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.JSlider;
import javax.swing.JScrollPane;
import javax.swing.JLabel;
public class Main2 {
private MyPanel mp = new MyPanel();
private JFrame frame;
private JSlider zoomSlider = new JSlider();
private JLabel zoomLabel = new JLabel("Zoom: XXX");
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main2 window = new Main2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Main2() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 619, 403);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpringLayout springLayout = new SpringLayout();
frame.getContentPane().setLayout(springLayout);
springLayout.putConstraint(SpringLayout.SOUTH, zoomSlider, 40, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, zoomSlider, -115, SpringLayout.EAST, frame.getContentPane());
zoomSlider.setValue(100);
zoomSlider.setSnapToTicks(true);
zoomSlider.setPaintTicks(true);
zoomSlider.setMaximum(200);
zoomSlider.setMinorTickSpacing(5);
zoomSlider.setMinimum(5);
springLayout.putConstraint(SpringLayout.NORTH, zoomSlider, 0, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomSlider, 0, SpringLayout.WEST, frame.getContentPane());
frame.getContentPane().add(zoomSlider);
JScrollPane scrollPane = new JScrollPane(mp);
springLayout.putConstraint(SpringLayout.NORTH, scrollPane, 10, SpringLayout.SOUTH, zoomSlider);
springLayout.putConstraint(SpringLayout.WEST, scrollPane, 10, SpringLayout.WEST, frame.getContentPane());
springLayout.putConstraint(SpringLayout.SOUTH, scrollPane, -10, SpringLayout.SOUTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.EAST, scrollPane, -10, SpringLayout.EAST, frame.getContentPane());
frame.getContentPane().add(scrollPane);
springLayout.putConstraint(SpringLayout.NORTH, zoomLabel, 10, SpringLayout.NORTH, frame.getContentPane());
springLayout.putConstraint(SpringLayout.WEST, zoomLabel, 6, SpringLayout.EAST, zoomSlider);
frame.getContentPane().add(zoomLabel);
frame.addWindowStateListener(new WindowStateListener() {
#Override
public void windowStateChanged(WindowEvent arg0) {
// TODO Auto-generated method stub
mp.draw();
}
});
zoomSlider.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
#Override
public void mouseEntered(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mouseReleased(MouseEvent arg0) {
int temp = (zoomSlider.getValue())-zoomSlider.getValue()%5;
setZoom(temp);
mp.draw();
}
});
mp.addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getPreciseWheelRotation() < 0) {
setZoom(zoomSlider.getValue()- 5);
} else {
setZoom(zoomSlider.getValue()+ 5);
}
// zoom += e.getPreciseWheelRotation();
if (mp.getZoom()*100 < 10) {
setZoom(10);
}
mp.draw();
}
});
AdjustmentListener adj = new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
setZoom(zoomSlider.getValue());
mp.draw();
}
};
scrollPane.getVerticalScrollBar().addAdjustmentListener(adj);
scrollPane.getHorizontalScrollBar().addAdjustmentListener(adj);
}
public void setZoom(int n) {
mp.setZoom(n);
zoomSlider.setValue(n);
zoomLabel.setText("Zoom: "+mp.getZoom()+"x");
}
}
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
int a, b;
String temp;
Float x, y;
x=(getZoom()*width); //Size of the page adjusted by zoom
y=(getZoom()*height);
temp = x.toString();
String temp1[] = temp.split("\\."); // converted to string to convert it to int
a = Integer.valueOf(temp1[0])+2*borderSize; //that value + 2 BorderSize
temp = y.toString();
String temp2[] = temp.split("\\.");
b = Integer.valueOf(temp2[0])+2*borderSize;
return new Dimension (a,b);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d = putZoom(g2d);
g2d.setColor(Color.WHITE);
g2d.fillRect(this.getX(),this.getY(), width, height);
g2d.setColor(Color.BLACK);
g2d.drawRect(this.getX()+borderSize,this.getY()+borderSize,width-2*borderSize,height-2*borderSize);
g2d.dispose();
}
public Graphics2D putZoom(Graphics2D g) {
AffineTransform at = new AffineTransform();
at.translate(borderSize,borderSize); // put the page a borderSize from the upper-left corner
at.scale(getZoom(),getZoom()); //adjust the page as zoom
Graphics2D g2d = g;
g2d.setTransform(at);
return g2d;
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {
return Float.valueOf(zoom)/100;
}
public void setZoom(int zom) { //this method is to update Zoom from the main
zoom=zom;
String zoomheight []= (String.valueOf(getZoom()*height)).split("\\.");
heightz = Integer.valueOf(zoomheight[0]);
String zoomwidth []= (String.valueOf(getZoom()*width)).split("\\.");
widthz = Integer.valueOf(zoomwidth[0]);
}
public int getZoomInt() {
return this.zoom;
}
}
Zoom(values from 0.1 to 2.0).
How can i improve this? Also, i have no idea how to update the JScrollPane's scrollbars together with the zoom.Thanks for the help.
UPDATE: i've created a minimal reproducible exemple.
Introduction
I started working on this before you updated your question. I used a zoom percentage rather than a zoom factor.
I created the following GUI and set the initial state to 30 percent.
I made the inner JPanel a checkerboard so you can more easily see the zoom. I modified your initial values so the inner JPanel would represent an 8 1/2 x 11 piece of paper at 50 pixels per inch.
Here's the same GUI at 100 percent.
Here's the same GUI at 10 percent.
Explanation
I created a JFrame and a control JPanel to hold the JSlider. I used a GridLayout to create the control JPanel.
I created an inner JPanel to hold the drawing and a display JPanel that holds the JScrollPane. I made the display JPanel proportionate to the size of the inner JPanel so I wouldn't have any stretching issues.
Getting the GUI to revalidate / repaint turned out to be the biggest challenge. I wound up having to invalidate the JScrollPane, both JScrollBars, and the display JPanel. I also had to reset the JScrollBars to zero each time I changed the zoom percentage.
Code
Here's the complete runnable code. I made all of the classes inner classes so I could post this as one code block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ZoomJPanelGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new ZoomJPanelGUI());
}
private int zoomPercentage = 30;
private DisplayPanel displayPanel;
private JFrame frame;
#Override
public void run() {
frame = new JFrame("Zoom JPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createControlPanel(), BorderLayout.BEFORE_FIRST_LINE);
this.displayPanel = new DisplayPanel(zoomPercentage);
frame.add(displayPanel.getPanel(), BorderLayout.AFTER_LAST_LINE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createControlPanel() {
JPanel panel = new JPanel(new GridLayout(0, 1));
panel.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
JLabel label = new JLabel("Zoom Percentage");
label.setFont(panel.getFont().deriveFont(Font.BOLD, 24f));
panel.add(label);
JSlider slider = new JSlider(
JSlider.HORIZONTAL, 10, 100, zoomPercentage);
slider.setFont(panel.getFont().deriveFont(16f));
slider.setMajorTickSpacing(30);
slider.setMinorTickSpacing(5);
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent event) {
JSlider slider = (JSlider) event.getSource();
if (!slider.getValueIsAdjusting()) {
zoomPercentage = (int) slider.getValue();
displayPanel.setZoomPercentage(zoomPercentage);
displayPanel.repaint();
frame.pack();
}
}
});
panel.add(slider);
return panel;
}
public class DisplayPanel {
private InnerPanel innerPanel;
private final JPanel panel;
private JScrollPane scrollPane;
private int zoomPercentage;
public DisplayPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
this.panel = createDisplayPanel();
}
private JPanel createDisplayPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
this.innerPanel = new InnerPanel(zoomPercentage);
scrollPane = new JScrollPane(innerPanel);
scrollPane.setPreferredSize(new Dimension(475, 600));
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
innerPanel.setZoomPercentage(zoomPercentage);
}
public JPanel getPanel() {
return panel;
}
public void repaint() {
innerPanel.repaint();
scrollPane.invalidate();
JScrollBar hScrollBar = scrollPane.getHorizontalScrollBar();
JScrollBar vScrollBar = scrollPane.getVerticalScrollBar();
hScrollBar.setValue(0);
vScrollBar.setValue(0);
hScrollBar.invalidate();
vScrollBar.invalidate();
panel.invalidate();
}
}
public class InnerPanel extends JPanel {
private static final long serialVersionUID = 1L;
private int maximumBorderSize = 25;
private int maximumCellSize = 50;
private int maximumHeight = 5500;
private int maximumWidth = 4250;
private int zoomPercentage;
public InnerPanel(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
public void setZoomPercentage(int zoomPercentage) {
this.zoomPercentage = zoomPercentage;
}
#Override
public Dimension getPreferredSize() {
int width = maximumWidth * zoomPercentage / 100;
int height = maximumHeight * zoomPercentage / 100;
return new Dimension(width, height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int borderSize = maximumBorderSize * zoomPercentage / 100;
paintBackground(g2d);
paintBorder(g2d, borderSize);
paintCheckerboard(g2d, borderSize);
}
private void paintBackground(Graphics2D g2d) {
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
private void paintBorder(Graphics2D g2d, int borderSize) {
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3f));
g2d.drawRect(borderSize, borderSize, getWidth() - 2 * borderSize,
getHeight() - 2 * borderSize);
}
private void paintCheckerboard(Graphics2D g2d, int borderSize) {
int cellSize = maximumCellSize * zoomPercentage / 100;
int width = maximumWidth - maximumBorderSize * 2 - 2;
int height = maximumHeight - maximumBorderSize * 2 - 2;
int cellWidth = width / maximumCellSize;
int cellHeight = height / maximumCellSize;
boolean isBlue = true;
int x = borderSize;
int y = borderSize;
int heightRemainder = height - cellHeight * cellSize;
for (int i = 0; i < cellHeight; i++) {
int widthRemainder = width - cellWidth * cellSize;
for (int j = 0; j < cellWidth; j++) {
if (isBlue) {
g2d.setColor(Color.BLUE);
} else {
g2d.setColor(Color.YELLOW);
}
isBlue = !isBlue;
g2d.fillRect(x, y, cellSize, cellSize);
x += cellSize;
if (widthRemainder > 0) {
x++;
widthRemainder--;
}
}
// isBlue = !isBlue;
x = borderSize;
y += cellSize;
if (heightRemainder > 0) {
y++;
heightRemainder--;
}
}
}
}
}
I've finally did it. Started by not using transformation for scaling it, but making a new draw with the size zoomed, adapting all sizes in the method setSizes(), and adjusting the Dimension by those sizes.
(Just changed this class)
class MyPanel extends JPanel{
private static final long serialVersionUID = -716735372803790424L;
int borderSize=28;
int zoom=100;
int height = 3565;
int width = 2537;
int widthz, heightz;
int maxHeight, maxWidth; //max size of draw
int maxAreaHeight, maxAreaWidth; //max size of area
public MyPanel() {
setBackground(Color.DARK_GRAY);
}
#Override
public Dimension getPreferredSize() {
setSizes();
return new Dimension (maxAreaWidth,maxAreaHeight);
}
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d= createBase(g2d);
}
public void draw() { //this method is to update the draw from the main
repaint();
}
public Float getZoom() {return Float.valueOf(zoom)/100;}
public void setZoom(int zom) {zoom=zom;}
public int getZoomInt() {return this.zoom;}
public void setSizes () {
widthz= width*getZoomInt()/100;
heightz=height*getZoomInt()/100;
maxHeight = heightz+2*borderSize;
maxWidth = widthz +2*borderSize;
maxAreaHeight = this.getY()+maxHeight;
maxAreaWidth = this.getX()+maxWidth;
if (this.getSize() != new Dimension(maxAreaWidth, maxAreaHeight)) {
this.setSize(maxAreaWidth, maxAreaHeight);
}
}
public Graphics2D createBase(Graphics2D g2d) {
Graphics2D g = g2d;
setSizes();
g.setColor(Color.WHITE);
g.fillRect(this.getX()+borderSize,this.getY()+borderSize, widthz, heightz);
g.setColor(Color.BLACK);
g.drawRect(this.getX()+borderSize+borderSize*zoom/100,this.getY()+borderSize+borderSize*zoom/100,widthz-2*borderSize*zoom/100,heightz-2*borderSize*zoom/100);
return g;
}
}
Thanks for all the help.

Issue with getting a graphical object to print to panel

I am having issues with the below program I have been working on. The program is supposed to take the users' input via a GUI and then use those to create and draw an object. After some testing I know that the object (either oval or rectangular) is being created. The issue I am running into is that the object is not being drawn within the panel. What am I doing wrong here and how can I get the draw method to be called. I am very new to the graphics methods and maybe making a simple mistake.
import java.awt.*;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
/**
*
* Purpose: This GUI will take a users input to print a shape
*/
public class Test {
//Shape class that extends the base rectangle class
public abstract static class Shape extends Rectangle {
private static final long serialVersionUID = 1L;
// instance variables for shape class
private String color;
private Boolean solid;
private int count;
// Constructor for shape class
public Shape(Rectangle rectangle, String color, Boolean solid)
{
super(rectangle);
this.color = color;
this.solid = solid;
count++;
}
// Method that will give the number of shapes printed
public int getNoOfShapes() {
return count;
}
// method that will take the users color select and set the color
public void setColor(Graphics g) {
if(color.equalsIgnoreCase("black"))
g.setColor(Color.BLACK);
else if(color.equalsIgnoreCase("red"))
g.setColor(Color.RED);
else if(color.equalsIgnoreCase("orange"))
g.setColor(Color.ORANGE);
else if(color.equalsIgnoreCase("yellow"))
g.setColor(Color.YELLOW);
else if(color.equalsIgnoreCase("green"))
g.setColor(Color.GREEN);
else if(color.equalsIgnoreCase("blue"))
g.setColor(Color.BLUE);
else if(color.equalsIgnoreCase("magenta"))
g.setColor(Color.MAGENTA);
}
// return shape type is solid or hollow
public Boolean getSolid() {
return solid;
}
// draw method
public abstract void draw(Graphics g);
}
//Oval subclass
public static class Oval extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for the oval class
public Oval(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Draw method for the oval
#Override
public void draw(Graphics g) {
// Draw a hollow oval shape
if(super.getSolid() == false)
g.drawOval((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
//Draw a solid oaval shape
else if(super.getSolid() == true)
g.fillOval((int)getX(),(int) getY(), (int)getWidth(), (int)getHeight());
}
}
//Rectangular subclass
public static class Rectangular extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for Rectangular class
public Rectangular(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Method to draw a rectangle
#Override
public void draw(Graphics g) {
// Will draw a hollow rectangle
if(super.getSolid() == false)
g.drawRect((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
//Will draw a solid rectangle
else if(super.getSolid() == true)
g.fillRect((int)getX(), (int)getY(), (int)getWidth(), (int)getHeight());
}
}
//Drawing class with GUI inside
public static class Drawing extends JPanel {
private static final long serialVersionUID = 1L;
// create Shape
private Shape shape1=null;
public void drawShape(Shape shape) throws OutsideBounds{
try {
if(shape.intersects(270,20,200,200))
throw new OutsideBounds("This shape will not fit into the drawing box");
}catch(OutsideBounds o) {
JOptionPane.showMessageDialog(null, o.getMessage());
}catch(Exception e) {
JOptionPane.showMessageDialog(null, "Please only enter integers ");
}
}
// Method to paint shape
#Override
public void paintComponent(Graphics g) {
try {
// print count of shapes
super.paintComponents(g);
shape1.setColor(g);
shape1.draw(g);
repaint();
}catch(Exception e) {}
}
}
public static class GUI{
// Instance variables for the GUI
// private JFrame window;
private JLabel shape;
private JLabel fillType;
private JLabel color;
private JLabel width;
private JLabel height;
private JLabel xCoordinate;
private JLabel yCoordinate;
private JLabel printCount;
private JComboBox<String> cshape;
private JComboBox<String> cfillType;
private JComboBox<String> ccolor;
private JTextField widthField;
private JTextField heightField;
private JTextField xField;
private JTextField yField;
private JButton draw;
private JButton clear;
private JButton exit;
private JPanel panel;
// Constructor that will initiate GUI variables
public GUI() {
JFrame window = new JFrame();
window.setSize(500,350); // set size fo frame
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setLayout(null); // set no layout
window.setLocationRelativeTo(null); // centered frame
window.setTitle("Geometric Drawing"); // title
shape=new JLabel("Shape Type");
fillType=new JLabel("Fill Type");
color=new JLabel("Color");
width=new JLabel("Width");
height=new JLabel("Height");
xCoordinate=new JLabel("x coordinate");
yCoordinate=new JLabel("y coordinate");
printCount=new JLabel("1");
cshape=new JComboBox<>(new String[] {"Oval","Rectangle"});
cfillType=new JComboBox<>(new String[] {"Solid","Hollow"});
ccolor=new JComboBox<>(new String[] {"Black","Red","Orange","Yellow","Green","Blue","Magenta"});
widthField=new JTextField();
heightField=new JTextField();
xField=new JTextField();
yField=new JTextField();
draw=new JButton("Draw");
clear = new JButton("clear");
exit = new JButton("exit");
panel=new JPanel();
// set border with title to panel
panel.setBorder(BorderFactory.createTitledBorder("Shape Drawing"));
// adding all parts to the window
window.add(shape);
window.add(fillType);
window.add(color);
window.add(width);
window.add(height);
window.add(xCoordinate);
window.add(yCoordinate);
window.add(printCount);
window.add(cshape);
window.add(cfillType);
window.add(ccolor);
window.add(widthField);
window.add(heightField);
window.add( xField);
window.add(yField);
window.add(draw);
window.add(clear);
window.add(exit);
window.add(panel);
// set positions within the window
shape.setBounds(20, 20, 100, 30);
fillType.setBounds(20, 55, 100, 30);
color.setBounds(20, 90, 100, 30);
width.setBounds(20, 125, 100, 30);
height.setBounds(20, 160, 100, 30);
xCoordinate.setBounds(20, 195, 100, 30);
yCoordinate.setBounds(20, 230, 100, 30);
printCount.setBounds(285, 40, 20, 30);
cshape.setBounds(140, 20, 100, 30);
cfillType.setBounds(140, 55, 100, 30);
ccolor.setBounds(140, 90, 100, 30);
widthField.setBounds(140, 125, 100, 30);
heightField.setBounds(140, 160, 100, 30);
xField.setBounds(140, 195, 100, 30);
yField.setBounds(140, 230, 100, 30);
draw.setBounds(230, 280, 80, 30);
clear.setBounds(120, 280,80,30);
exit.setBounds(330, 280,80,30);
panel.setBounds(270, 20, 200, 200);
// adding action listener to draw button
// draw.addActionListener((ActionListener) this);
window.setVisible(true); // make frame visible
draw.addActionListener(
new java.awt.event.ActionListener(){
public void actionPerformed(java.awt.event.ActionEvent evt){
try {
Drawing d1 = new Drawing();
Shape shape;
String color;
int xCoord = 0, yCoord = 0, width = 0, height = 0;
//Point accounting for the needed shift
xCoord = Integer.parseInt(xField.getText() + 270);
yCoord = Integer.parseInt(yField.getText() + 50);
width = Integer.parseInt(widthField.getText());
height = Integer.parseInt(heightField.getText());
Rectangle r1 = new Rectangle (xCoord, yCoord, width, height);
// Point p = new Point(shapeX+270, shapeY+50);
//setting dimensions based off user input
//Dimension d = new Dimension(shapeWidth, shapeHeight);
if(((String) cshape.getSelectedItem()).equalsIgnoreCase("oval")){
shape = new Oval(r1, (String)ccolor.getSelectedItem(), true);
System.out.print("creating oval");
}
else{
shape = new Rectangular(r1,(String)ccolor.getSelectedItem(), true);}
d1.drawShape(shape);
window.repaint(); // call paint() method
}catch(NumberFormatException e) {
JOptionPane.showMessageDialog(null, "Please only enter integers ");
} catch (OutsideBounds ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
//Action Listener for clear button
clear.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//Clearing all text fields
widthField.setText("");
heightField.setText("");
xField.setText("");
yField.setText("");
}
});
//Action Liistener for exit button
exit.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//message to the user
JOptionPane.showMessageDialog(null,"Program is ending");
window.dispose();}
});
}
}
// Out of bounds class
public static class OutsideBounds extends Exception {
private static final long serialVersionUID = 1L;
// parameterized constructor
public OutsideBounds(String errorMessage) {
// call super class parameterized constructor
super(errorMessage);
}
}
public static void main(String[] args) {
GUI G1 = new GUI(); // Creating GUI
}
}```
There are a number small problems, which are combining to produce a larger problem.
null layouts are just a bad idea. Take the time to learn how to use and mix appropriate layouts
Don't use String as a carrier of information if you can avoid it. It's too easy to screw up. In you case, learn how to make use of a ListCellRenderer to customise the JComoboBox so it can carry Color as it's base value
There should be a single instance of Drawing which should render ALL the shapes. You then add each new shape to it. Instead of trying to create new instances for each individual shape.
In order for something to be painted, it must be added to something which can be painted. This means that the Drawing panel needs to be added to your frame at some point (or a contained which is directly or indirectly contained within your frame).
This is a very basic re-work of your code. It will generate a oval or rectangle at a random location with a random color. I'll leave you to fill in the rest of your requirements around it
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class Test {
//Shape class that extends the base rectangle class
public abstract static class Shape extends Rectangle {
private static final long serialVersionUID = 1L;
// instance variables for shape class
private String color;
private Boolean solid;
private int count;
// Constructor for shape class
public Shape(Rectangle rectangle, String color, Boolean solid) {
super(rectangle);
this.color = color;
this.solid = solid;
count++;
}
// Method that will give the number of shapes printed
public int getNoOfShapes() {
return count;
}
// method that will take the users color select and set the color
public void setColor(Graphics g) {
if (color.equalsIgnoreCase("black")) {
g.setColor(Color.BLACK);
} else if (color.equalsIgnoreCase("red")) {
g.setColor(Color.RED);
} else if (color.equalsIgnoreCase("orange")) {
g.setColor(Color.ORANGE);
} else if (color.equalsIgnoreCase("yellow")) {
g.setColor(Color.YELLOW);
} else if (color.equalsIgnoreCase("green")) {
g.setColor(Color.GREEN);
} else if (color.equalsIgnoreCase("blue")) {
g.setColor(Color.BLUE);
} else if (color.equalsIgnoreCase("magenta")) {
g.setColor(Color.MAGENTA);
}
}
// return shape type is solid or hollow
public Boolean getSolid() {
return solid;
}
// draw method
public abstract void draw(Graphics g);
}
//Oval subclass
public static class Oval extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for the oval class
public Oval(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Draw method for the oval
#Override
public void draw(Graphics g) {
// Draw a hollow oval shape
if (super.getSolid() == false) {
g.drawOval((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
} //Draw a solid oaval shape
else if (super.getSolid() == true) {
g.fillOval((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
}
}
}
//Rectangular subclass
public static class Rectangular extends Shape {
private static final long serialVersionUID = 1L;
// Constructor for Rectangular class
public Rectangular(Rectangle rectangle, String color, Boolean solid) {
super(rectangle, color, solid);
}
//Method to draw a rectangle
#Override
public void draw(Graphics g) {
// Will draw a hollow rectangle
if (super.getSolid() == false) {
g.drawRect((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
} //Will draw a solid rectangle
else if (super.getSolid() == true) {
g.fillRect((int) getX(), (int) getY(), (int) getWidth(), (int) getHeight());
}
}
}
//Drawing class with GUI inside
public static class Drawing extends JPanel {
private List<Shape> shapes;
private List<String> colors;
private final Random rnd = new Random();
public Drawing() {
shapes = new ArrayList<>(25);
colors = new ArrayList<>();
colors.add("black");
colors.add("red");
colors.add("orange");
colors.add("yellow");
colors.add("green");
colors.add("blue");
colors.add("magenta");
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Rectangle randomBounds() {
int minSize = 10;
int avaliableWidth = getWidth() - minSize;
int avaliableHeight = getHeight() - minSize;
int width = Math.max(minSize, rnd.nextInt(avaliableWidth / 4));
int height = Math.max(minSize, rnd.nextInt(avaliableHeight / 4));
int x = rnd.nextInt(avaliableWidth - width);
int y = rnd.nextInt(avaliableHeight - height);
Rectangle bounds = new Rectangle(x, y, width, height);
return bounds;
}
protected String randomColor() {
Collections.shuffle(colors);
String color = colors.get(0);
return color;
}
public void addCircle() {
shapes.add(new Oval(randomBounds(), randomColor(), rnd.nextBoolean()));
repaint();
}
public void addRectangle() {
shapes.add(new Rectangular(randomBounds(), randomColor(), rnd.nextBoolean()));
repaint();
}
// Method to paint shape
#Override
public void paintComponent(Graphics g) {
// print count of shapes
super.paintComponents(g);
for (Shape shape : shapes) {
Graphics2D g2d = (Graphics2D) g.create();
shape.setColor(g2d);
shape.draw(g2d);
g2d.dispose();
}
}
}
public static class GUI {
private Drawing drawing;
// Constructor that will initiate GUI variables
public GUI() {
JFrame window = new JFrame();
window.setDefaultCloseOperation(EXIT_ON_CLOSE);
window.setTitle("Geometric Drawing"); // title
JPanel controls = new JPanel(new GridLayout(0, 1));
controls.add(makeButton("Circle"));
controls.add(makeButton("Rectangle"));
drawing = new Drawing();
window.add(controls, BorderLayout.WEST);
window.add(drawing);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true); // make frame visible
}
protected JButton makeButton(String name) {
JButton btn = new JButton(name);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if ("Circle".equals(e.getActionCommand())) {
drawing.addCircle();
} else if ("Rectangle".equals(e.getActionCommand())) {
drawing.addRectangle();
}
}
});
return btn;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
GUI G1 = new GUI(); // Creating GUI
}
});
}
}
}
Take a look at:
Laying Out Components Within a Container
How to Use Combo Boxes
for more details

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

JlayeredPane Transparency setOpaque not working

I am creating a simulation type application, I want one background layer and another layer on top for all of the animations. Im currently using JlayeredPanes but i cannot get the background on the top layer to show as transparent so I can see the background, any help is much appreciated, heres the code:
Background layer
public class SimBackground extends JLayeredPane{
private Model theModel;
private SimulationArea simulationArea;
public SimBackground(Model theModel){
this.theModel=theModel;
setBackground(new Color(0, 230, 0));
setOpaque(true);
setPreferredSize(new Dimension(500,500));
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for(int x=0;x<50;x++){
for(int y=0;y<50;y++){
g.drawRect((x*10), (y*10), 10, 10);
}
}
}
Top layer
public class SimulationArea extends JLayeredPane {
private int SPEED = 100;
private Model theModel;
Timer timer;
public SimulationArea(Model theModel){
this.theModel = theModel;
setPreferredSize(new Dimension(500,500));
setLocation(0,0);
setOpaque(false);
setBackground(new Color(0,0,0,0));
setVisible(true);
//Swing Timer
timer = new Timer(SPEED,new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
update();
repaint();
revalidate();
}
});
}
private void update() {
theModel.update();
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
//test get 1 active object
ArrayList<ActiveObject> activeObjects = theModel.getActiveObjects();
//System.out.println(activeObjects.size());
for(int i=0; i<activeObjects.size(); i++){
ActiveObject activeObject = theModel.getActiveObjects().get(i);
int x = activeObject.getCoordinates().getX();
int y = activeObject.getCoordinates().getY();
int size = activeObject.getSize();
g2d.fillRect (x ,y , size, size);
}
}
Can someone please tell me what i'm missing here?
Don't use a JLayeredPane, but if you do need to use JLayeredPane in the future, you will want to read the tutorial here since as per my comment you're not using them correctly at all. Instead I recommend that you simplify by doing all drawing in a single JPanel, drawing your background into a BufferedImage perhaps in the constructor, and then drawing that image and your sprites within the 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.image.BufferedImage;
import javax.swing.*;
public class SimExample extends JPanel {
private static final int PREF_W = 500;
private static final int PREF_H = PREF_W;
private static final Color BKGD_COLOR = new Color(0, 230, 0);
private BufferedImage bkgrnd = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
public SimExample() {
Graphics2D g = bkgrnd.createGraphics();
g.setBackground(BKGD_COLOR);
g.clearRect(0, 0, PREF_W, PREF_H);
g.setColor(Color.black);
for (int x = 0; x < 50; x++) {
for (int y = 0; y < 50; y++) {
g.drawRect((x * 10), (y * 10), 10, 10);
}
}
g.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (bkgrnd != null) {
g.drawImage(bkgrnd, 0, 0, null);
}
// draw sprites here
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
SimExample mainPanel = new SimExample();
JFrame frame = new JFrame("SimExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Categories

Resources