i have a simple question but for some reason I'm stuck and cant figure it out.
I have a simple code with 3 buttons in a JPanel each one of the buttons should draw at the JPanel with the overriden PaintComponent and it does but What i want to do is make the shapes stay.
(after i will change it to random positions )
any help would be apreciated thank you!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Ex3 extends JFrame implements ActionListener
{
JButton circle, triang, square;
JPanel jp, jButton;
boolean drawSq, drawCir, drawTr;
public static void main(String[] args)
{
JFrame frame = new Ex3();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public Ex3()
{
super("Ex3");
setSize(400, 500);
circle = new JButton("Circle");
triang = new JButton("Triangle");
square = new JButton("Square");
drawCir = false;
drawSq = false;
drawTr = false;
//setLayout(new FlowLayout(FlowLayout.CENTER, 20, 30));
jButton = new JPanel();
jp = new CustomPanel();
circle.addActionListener(this);
triang.addActionListener(this);
square.addActionListener(this);
jButton.add(circle);
jButton.add(triang);
jButton.add(square);
jButton.add(new JLabel("Kayy"));
jp.setBackground(Color.gray);
add(jp, BorderLayout.CENTER);
add(jButton, BorderLayout.NORTH);
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == circle)
{
drawCir = true;
System.out.println("created cricle");
}
if(e.getSource() == triang)
{
drawTr = true;
System.out.println("created triangle");
}
if(e.getSource() == square)
{
drawSq = true;
System.out.println("created square");
}
repaint();
}
class CustomPanel extends JPanel
{
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if(drawSq)
{
g.setColor(Color.blue);
g.drawRect(((int)Math.random()* 20), 50, 200, 150);
drawSq = false;
}
else if(drawTr)
{
g.setColor(Color.green);
}
else if(drawCir)
{
g.setColor(Color.red);
g.drawOval(182, 124, 200, 200);
}
else
{
}
//drawSq = false;
drawTr = false;
drawCir = false;
}
}
}
What i want to do is make the shapes stay
Then you need to repaint the shapes every time the component is repainted.
There are two common approaches to do this:
paint to a BufferedImage
keep a list of Object to paint and iterate through the list to paint each object
Check out Custom Painting Approaches for working examples of each approach. Which approach you use will depend on your exact requirements.
Also, you should never change the state of your component in the painting method. You can't control when the paintComponent() method is invoked. Sometimes the system will repaint a component. So the presence of your Boolean variables indicates a design problem.
Related
So, I made my first useful app - a paint and drawing tool, but... It can't be runned (started). I don't know, is the problem in my computer or in the code... Here is the code:
package MaddpawNightpainter;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Nightpainter2 {
JButton clearBtn, blackBtn, blueBtn, greenBtn, redBtn, magentaBtn;
Nightpainter drawArea;
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearBtn) {
drawArea.clear();
} else if (e.getSource() == blackBtn) {
drawArea.black();
} else if (e.getSource() == blueBtn) {
drawArea.blue();
} else if (e.getSource() == greenBtn) {
drawArea.green();
} else if (e.getSource() == redBtn) {
drawArea.red();
} else if (e.getSource() == magentaBtn) {
drawArea.magenta();
}
}
};
public static void main(String[] args) {
new Nightpainter().show();
}
public void show() {
// create main frame
JFrame frame = new JFrame("Nightpainter 1.0");
Container content = frame.getContentPane();
// set layout on content pane
content.setLayout(new BorderLayout());
// create draw area
drawArea = new Nightpainter();
// add to content pane
content.add(drawArea, BorderLayout.CENTER);
// create controls to apply colors and call clear feature
JPanel controls = new JPanel();
clearBtn = new JButton("Clear");
clearBtn.addActionListener(actionListener);
blackBtn = new JButton("Black");
blackBtn.addActionListener(actionListener);
blueBtn = new JButton("Blue");
blueBtn.addActionListener(actionListener);
greenBtn = new JButton("Green");
greenBtn.addActionListener(actionListener);
redBtn = new JButton("Red");
redBtn.addActionListener(actionListener);
magentaBtn = new JButton("Magenta");
magentaBtn.addActionListener(actionListener);
// add to panel
controls.add(greenBtn);
controls.add(blueBtn);
controls.add(blackBtn);
controls.add(redBtn);
controls.add(magentaBtn);
controls.add(clearBtn);
// add to content pane
content.add(controls, BorderLayout.NORTH);
frame.setSize(500, 600);
// can close frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// show the swing paint result
frame.setVisible(true);
// :)
}
}
And the other file:
package MaddpawNightpainter;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JComponent;
/*
Author: Bogdan Ganev
Title: Maddpaw Nightpainter
Description: Nightpainter is a drawing tool, part of Maddpaw - Multifunctional App for Designers,
Developers And Writers
Copyright: Copyright (C) 2016 Bogdan Ganev. All rights reserved. Maddpaw, Multifunctional App for Designers,
Developers And Writers is a trademark of Bogdan Ganev. Java TM is a trademark of Oracle Corporation (R)
*/
public class Nightpainter extends JComponent {
// Image in which we're going to draw
private Image image;
// Graphics2D object ==> used to draw on
private Graphics2D g2;
// Mouse coordinates
private int currentX, currentY, oldX, oldY;
public Nightpainter() {
setDoubleBuffered(false);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
// save coord x,y when mouse is pressed
oldX = e.getX();
oldY = e.getY();
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
// coord x,y when drag mouse
currentX = e.getX();
currentY = e.getY();
if (g2 != null) {
// draw line if g2 context not null
g2.drawLine(oldX, oldY, currentX, currentY);
// refresh draw area to repaint
repaint();
// store current coords x,y as olds x,y
oldX = currentX;
oldY = currentY;
}
}
});
}
protected void paintComponent(Graphics g) {
if (image == null) {
// image to draw null ==> we create
image = createImage(getSize().width, getSize().height);
g2 = (Graphics2D) image.getGraphics();
// enable antialiasing
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// clear draw area
clear();
}
g.drawImage(image, 0, 0, null);
}
// now we create exposed methods
public void clear() {
g2.setPaint(Color.white);
// draw white on entire draw area to clear
g2.fillRect(0, 0, getSize().width, getSize().height);
g2.setPaint(Color.black);
repaint();
}
public void red() {
// apply red color on g2 context
g2.setPaint(Color.red);
}
public void black() {
g2.setPaint(Color.black);
}
public void magenta() {
g2.setPaint(Color.magenta);
}
public void green() {
g2.setPaint(Color.green);
}
public void blue() {
g2.setPaint(Color.blue);
}
}
So is the problem in my computer or in the code? By the way, it's 10,3 KB...
Change the code in the NightPainter2 class to this:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Nightpainter2 {
static JButton clearBtn, blackBtn, blueBtn, greenBtn, redBtn, magentaBtn;
static Nightpainter drawArea;
static ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearBtn) {
drawArea.clear();
} else if (e.getSource() == blackBtn) {
drawArea.black();
} else if (e.getSource() == blueBtn) {
drawArea.blue();
} else if (e.getSource() == greenBtn) {
drawArea.green();
} else if (e.getSource() == redBtn) {
drawArea.red();
} else if (e.getSource() == magentaBtn) {
drawArea.magenta();
}
}
};
public static void main(String[] args) {
// create main frame
JFrame frame = new JFrame("Nightpainter 1.0");
// set layout on content pane
frame.setLayout(new BorderLayout());
// create draw area
drawArea = new Nightpainter();
// add to content pane
frame.add(drawArea, BorderLayout.CENTER);
// create controls to apply colors and call clear feature
JPanel controls = new JPanel();
clearBtn = new JButton("Clear");
clearBtn.addActionListener(actionListener);
blackBtn = new JButton("Black");
blackBtn.addActionListener(actionListener);
blueBtn = new JButton("Blue");
blueBtn.addActionListener(actionListener);
greenBtn = new JButton("Green");
greenBtn.addActionListener(actionListener);
redBtn = new JButton("Red");
redBtn.addActionListener(actionListener);
magentaBtn = new JButton("Magenta");
magentaBtn.addActionListener(actionListener);
// add to panel
controls.add(greenBtn);
controls.add(blueBtn);
controls.add(blackBtn);
controls.add(redBtn);
controls.add(magentaBtn);
controls.add(clearBtn);
// add to content pane
frame.add(controls, BorderLayout.NORTH);
frame.setSize(500, 600);
// can close frame
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// show the swing paint result
frame.setVisible(true);
// :)
}
I don't know why it worked, I just saw a line through 'show' in new NightPainter2.show(); so tried this. Didn't expect it to work, honestly :)
And you may want to add frame.setLocationRelativeTo(null); to make it open in the center of the screen.
I never worked with Timers before so my problem is probably stupid one really. My program draws a circle which is red and after random seconds the circle should change its color to green. I just made a swing timer as you can see below in the code. And it enters actionPerformed() method but it doesn't change color. Could you help me somehow fix my problem with changing colors?
My code:
package igrica;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ChangingCircle implements ActionListener{
JFrame frame;
Timer timer;
Random r;
public static void main(String[] args) {
ChangingCircle gui = new ChangingCircle();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.red);
g.fillOval(100, 100, 100, 100);
Random r = new Random();
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.out.println("Timer out");
g.setColor(Color.green);
g.fillOval(100, 100, 100, 100);
}
});
timer.start();
}
}
}
There's quite the mess in your code. Try this:
public class ChangingCircle {
Color color = Color.RED;
MyPanel panel = new MyPanel();
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
ChangingCircle gui = new ChangingCircle();
gui.go();
});
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
Random r = new Random();
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
System.out.println("Timer");
color = Color.GREEN;
panel.repaint();
}
});
timer.setRepeats(false);
timer.start();
}
class MyPanel extends JPanel {
private int size = 100, loc = 100;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillOval(loc, loc, size, size);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(size + loc, size + loc);
}
}
}
The idea is that the timer only changes the property of the shape to be drawn and then calls repaint() to reflect the change. The paintComponent is called whenever it is needed, even in quick succession and should return quickly.
Specific Notes:
Start Swing from the EDT.
Create and start the timer from outside of paintComponent since it is called many times and that will create and start many timers.
You should probably set the timer not to repeat.
Call super.paintComponent(g); as the first thing inside paintComponent.
You seem to have an ActionListener that does nothing.
General tips:
Use the #Override annotation when applicable.
Call pack() on the frame instead of setting its size manually and #Override the getPreferredSize method of the component you paint on. Return a meaningful size based on what you draw.
Use add(component, location) and not the other way around (deprecated).
Don't use fields when local variables will do (Random r for example).
Use uppercase constant names (Color.RED instead of Color.red).
Don't initiate a Timer from within a paintComponent method. This method should be for painting and painting only. Instead start the Timer in your constructor and within your Timer's actionPerromed and call repaint(), change the state of a field of the class, and use that information within the paintComponent use that field to draw any new information.
e.g.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class ChangingCircle {
JFrame frame;
public static void main(String[] args) {
ChangingCircle gui = new ChangingCircle();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300, 300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
class MyPanel extends JPanel {
private Random r = new Random();
private boolean draw = false;
public MyPanel() {
Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() {
public void actionPerformed(ActionEvent ev) {
draw = true;
repaint();
}
});
timer.setRepeats(false);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (draw) {
g.setColor(Color.red);
g.fillOval(100, 100, 100, 100);
}
}
}
}
Also, don't forget to call the super's paintComponent method from within your override.
If you need to change colors, give the JPanel a Color field, say called color and change it's value from within the Timer, and then call repaint(). Again within paintComponent, use the value of that field to draw the oval with. Also in this situation, the Timer should repeat, so get rid of timer.setRepeats(false) in that situation.
The timer works asynchronously and paintComponent finishes before finishing the work of timer.
I am attempting to rotate a GridLayout filled with text labels to simulate a portrait orientation view due to an OS restriction. The JPanel they are inside of is not square, so when rotating 90 degrees the labels cut off based on dimensions of the JPanel. Is it possible to resize the layout based on the rotation to still fit within the JPanel? Researching into this showed many options for rotations, but only for square JPanels.
To further explain my problem: when I rotate the labels painted inside they stay formatted to the normal oriented x,y, and I want it to format the layout to fit into the 90 degree rotated x,y (so basically y and x are flipped). currently a portion of my grid is cut off after rotating. Also the final display should fit all 13 by 24 letters filled in the current JPnel.
edit: Using vague comments shows I need to paint after rotating, but doing so crops the grid and does not fill back to my preferred size.
JPanel code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Screen extends JPanel {
private JLabel[][] labels = new JLabel[13][24];
private GridLayout layout;
public Screen() {
//setLocation(315,35);
layout = new GridLayout(13, 24);
layout.preferredLayoutSize(this);
//setBounds(315, 65, 243, 350);
setBounds(315, 65, 243, 350);
setLayout(layout);
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 24; j++) {
labels[i][j] = new JLabel();
labels[i][j].setBackground(Color.BLACK);
add(labels[i][j]);
}
}
//testing new letter
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 24; j++) {
labels[i][j].setText("T");
labels[i][j].setForeground(Color.GREEN);
}
}
setBackground(Color.black);
setVisible(true);
repaint();
}
#Override
public void paintComponent(Graphics g) {
//Rotates screen graphics to correct orientation
Graphics2D g2d = (Graphics2D) g;
int w2 = getWidth() / 2;
int h2 = getHeight() / 2;
g2d.rotate(Math.PI / 2, w2, h2);
super.paintComponent(g);
setSize(243,350);
}
}
test code:
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
public class RotateTest {
public static class Frame extends JFrame {
public Frame() {
Screen screen = new Screen();
JLayeredPane pane = new JLayeredPane();
setUndecorated(false);
setSize(800, 480);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pane.add(screen, 0, 0);
pane.setVisible(true);
add(pane);
}
}
public static void main(String[] args) {
Frame frame = new Frame();
}
}
The process of rotating a component is more complicated then just painting the rotated image. There are a number of interconnected layers which generate contractual obligations.
For example, the size of the clipping rectangle set to the Graphics context that is passed to your component for painting is determined by the current size of the component, this size is calculated by the layout manager, but may consider the preferred size of the individual component...
That's a lot of re-wiring that needs to be considered...call my lazy, but if I can find a ready made solution, I'd prefer to use it, so based on this example, I can generate the following...
The red LineBorder around the field panel is there to show that the entire component is been rotated, not just it's children. The use of pack also demonstrates that this solution is still honouring it's contractual obligations to the rest of the API
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class RotateExample {
public static void main(String[] args) {
new RotateExample();
}
public RotateExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ExamplePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ExamplePane extends JPanel {
private FieldPane fieldPane;
private DefaultTransformModel transformModel;
private JButton rotate;
private double angle;
public ExamplePane() {
setLayout(new BorderLayout());
rotate = new JButton("Rotate");
rotate.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
transformModel.setRotation(Math.toRadians((angle += 90)));
SwingUtilities.getWindowAncestor(ExamplePane.this).pack();
}
});
fieldPane = new FieldPane();
transformModel = new DefaultTransformModel();
transformModel.setRotation(Math.toRadians(0));
transformModel.setScaleToPreferredSize(true);
JXLayer<JComponent> rotatePane = TransformUtils.createTransformJXLayer(fieldPane, transformModel);
JPanel content = new JPanel(new GridBagLayout());
content.add(rotatePane);
add(rotate, BorderLayout.SOUTH);
add(content);
}
}
public class FieldPane extends JPanel {
public FieldPane() {
setBorder(new LineBorder(Color.RED));
setLayout(new GridBagLayout());
JTextField field = new JTextField(10);
field.setText("Hello world");
add(field);
}
}
}
Caveats
This requires JXLayer (I was using version 3), SwingX (I was using version 1.6.4) and Piet Blok's excellent examples, which no longer seem to be available on the net...
I've put all the source code of JXLayer (version 3) and Piet's examples into a single zip and I would suggest, if you are interested, you grab a copy and store it some where safe.
You will also need JHLabs filters
Updated
And using your Screen panel (without the custom painting)...
A bit of context - I am creating a rudimentary implementation of Scrabble and the GUI relies on Java Swing and AWT. The code excerpt below contains the constructor for the Cell class (individual space on the Scrabble board). I am in the proof of concept phase and am testing the addition and removal of a hard-coded letter icon to an individual cell. Each cell is an individual JPanel with a JLabel (which, contains an ImageIcon of the letter). The code looks as though it works without error, BUT every 5-6 additions/removals (via mouse click) causes a class cast exception. The specific exception is:
Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: Cell cannot be cast to javax.swing.JLabel
I can't see where this exception would be caused, but more specifically why it only occurs after multiple successful additions and removals. Any insight greatly appreciated; I am a beginner to Java GUI.
public class Cell extends JPanel {
/*Tile Colors*/
public static Color twColor = new Color(255, 0, 0);
public static Color dwColor = new Color(255, 153, 255);
public static Color tlColor = new Color(0, 51, 255);
public static Color dlColor = new Color(102, 204, 255);
public static Color defaultColor = new Color(255, 255, 255);
private JLabel selected = null;
private JLabel clicked = null;
private JLabel letterIcon;
private ImageIcon defaultIcon;
private ImageIcon testImg;
public Cell(int xPos, int yPos, int premiumStatus) {
defaultIcon = new ImageIcon ("img/transparent.png");
testImg = new ImageIcon ("img/test.jpg"); // Letter image hard-coded for testing
letterIcon = new JLabel("", defaultIcon, JLabel.CENTER);
add(letterIcon);
letterIcon.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
JLabel clicked = (JLabel) getComponentAt(e.getPoint());
System.out.println(clicked);
if (clicked == null) {
return;
}
if (selected != null) {
selected.removeAll();
selected.revalidate();
selected.setIcon(defaultIcon);
selected.repaint();
System.out.println("Test");
selected = null;
return;
}
if (selected == null) {
selected = clicked;
selected.setIcon(testImg);
selected.revalidate();
selected.repaint();
}
}
});
}
The problem is being cause by calling getComponentAt(e.getPoint()); on the Cell, when the mouse coordinates have already been converted to the coordinate space of the letterIcon.
When a component is clicked, the MouseEvent's point is automatically converted to the coordinate space of the component that the listener is registered to.
In your case, that is the letterIcon. This means that a point at 0x0 is the top/left corner of the letterIcon (despite where it might physically be positioned).
So, calling getComponentAt(e.getPoint()) is ask the Cell to return the component that corresponds to a position which is actually relative only to the letterIcon, which will (in most cases) return the Cell itself.
Instead, you should be simply using MouseEvent#getComponent to return the component that triggered the event, which will be the letterIcon
Update with a simple example
This is a simple example that sets up a JLabel as a mouse target. When the mouse is clicked, both the label and it's parent container will paint a small dot based on the coordinates of the mouse click.
There is the added benefit that the parent container will also translate the click point to it's coordinate space and paint a second dot, which should be in the same click as the labels.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestMouseClicked {
public static void main(String[] args) {
new TestMouseClicked();
}
public TestMouseClicked() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel clickMe;
private Point clickPoint;
public TestPane() {
setLayout(new GridBagLayout());
clickMe = new JLabel("Click me") {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.MAGENTA);
// paintPoint(g, clickPoint);
}
};
add(clickMe);
clickMe.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
clickPoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
paintPoint(g, clickPoint);
if (clickPoint != null) {
g.setColor(Color.BLUE);
// Convert the point from clickMe coordinate space to local coordinate space
paintPoint(g, SwingUtilities.convertPoint(clickMe, clickPoint, this));
}
}
protected void paintPoint(Graphics g, Point clickPoint) {
if (clickPoint != null) {
int size = 4;
g.fillOval(clickPoint.x - size, clickPoint.y - size, size * 2, size * 2);
}
}
}
}
So I have this problem with my code. Whenever I load up the game there is a red square in the center of the screen, and I have not programmed it to do so. I have tried to find the error for hours but I just can't see it. I think it has to do with the panels or something. The second thing is that when I press the button to draw the grid, only a small line appears. It is programmed to be much bigger than what it is, and it is not in the right location either. Below is all my code, and any help is greatly appreciated!!
package com.theDevCorner;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JPanel;
public class Game extends JPanel implements ActionListener {
public static JButton grid = new JButton("Show Grid");
public static JPanel drawArea = new JPanel();
public static JMenuBar menu = new JMenuBar();
public static JPanel notDrawn = new JPanel();
public static boolean gridPressed = false;
public Game() {
grid.addActionListener(this);
}
public static void main(String args[]) {
Game game = new Game();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(new Dimension(
Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit
.getDefaultToolkit().getScreenSize().height));
frame.setTitle("Game");
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
menu.setSize(new Dimension(1600, 20));
menu.setLocation(0, 0);
notDrawn.setBackground(new Color(255, 0, 50));
notDrawn.setSize(100, 900);
notDrawn.add(grid);
notDrawn.setLayout(null);
grid.setSize(new Dimension(100, 25));
grid.setLocation(0, 25);
drawArea.setSize(new Dimension((Toolkit.getDefaultToolkit()
.getScreenSize().width), Toolkit.getDefaultToolkit()
.getScreenSize().height));
drawArea.setLocation(100, 0);
drawArea.setBackground(Color.black);
drawArea.add(menu);
drawArea.add(game);
frame.add(drawArea);
frame.add(notDrawn);
}
public void paint(Graphics g) {
Game game = new Game();
if (gridPressed) {
Game.drawGrid(0, 0, g);
}
g.dispose();
repaint();
}
public static void drawGrid(int x, int y, Graphics g) {
g.setColor(Color.white);
g.drawLine(x, y, 50, 300);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == grid && gridPressed == true) {
gridPressed = false;
System.out.println("Unpressed");
}
if (e.getSource() == grid) {
gridPressed = true;
System.out.println("Pressed");
}
}
}
There are a number of problems...
The red "square" you are seeing is actually your grid button. The reason it's red is because of your paint method.
Graphics is a shared resource, that is, each component that is painted on the screen shares the same Graphics context. Because you chose to dispose of the context and because you've failed to honor the paint chain, you've basically screwed it up.
Don't EVER dispose of a Graphics context you didn't create. It will prevent anything from being painted to it again. Always call super.paintXxx. The paint chain is complex and does a lot of very important work. If you're going to ignore it, be ready to have to re-implement it.
null layouts are vary rarely the right choice, especially when you're laying out components. You need to separate your components from your custom painting, otherwise the components will appear above the custom painting.
This frame.setSize(new Dimension(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height)) is not the way to maximize a window. This does not take into consideration the possibility of things like tasks bars. Instead use Frame#setExtendedState
As Andreas has already commented, you actionPerformed logic is wrong. You should be using an if-statement or simply flipping the boolean logic...
Updated with simple example
ps- static is not your friend here...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel implements ActionListener {
private GridPane gridPane;
public Game() {
setLayout(new BorderLayout());
SideBarPane sideBar = new SideBarPane();
sideBar.addActionListener(this);
add(sideBar, BorderLayout.WEST);
gridPane = new GridPane();
add(gridPane);
}
public static void main(String args[]) {
Game game = new Game();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setTitle("Game");
frame.setAlwaysOnTop(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(game);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equalsIgnoreCase("grid")) {
gridPane.setGridOn(!gridPane.isGridOn());
}
}
public class GridPane extends JPanel {
private boolean gridOn = false;
public GridPane() {
setBackground(Color.BLACK);
}
public boolean isGridOn() {
return gridOn;
}
public void setGridOn(boolean value) {
if (value != gridOn) {
this.gridOn = value;
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (gridOn) {
g.setColor(Color.white);
g.drawLine(0, 0, 50, 300);
}
}
}
public class SideBarPane extends JPanel {
public JButton grid;
public SideBarPane() {
setBackground(new Color(255, 0, 50));
setLayout(new GridBagLayout());
grid = new JButton("Show Grid");
grid.setActionCommand("grid");
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.weighty = 1;
add(grid, gbc);
}
public void addActionListener(ActionListener listener) {
grid.addActionListener(listener);
}
}
}