I am trying to write a program in Java Swing that outputs a 10 x 10 grid of geometric rectangles filled with randoms colors. However, when the user clicks on one of the rectangles within the display window the rectangle should repaint() and change to another color.
Thus far I have the rudimentary program running, but I can't figure out how to implement a mouseListener to it in order to have the rectangles' color change when a user clicks inside. At this point, the rectangles only repaint when the display window is expanded and minimized. Any advice/help would be greatly appreciated! Thanks!
Here is what I have so far...
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.geom.*;
public class ColorGrid extends JPanel {
int w, x, y, z;
Color c = new Color((int)(Math.random() * 0xFFFFFF));
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
setLayout(new GridLayout(10,10));
int w = x = y = z = 0;
for(int i=0;i<100;i++){
Color c = new Color((int)(Math.random() * 0xFFFFFF));
w+=10;
x+=10;
y+=50;
z+=15;
g2.drawRect(w+10,x+30,y,z);
g2.drawRect(w+10,x+30,y,z);
g2.fillRect(w+10,x+30,y,z);
g2.setPaint(c);
}
}
public static void main(String[] args) {
JFrame f= new JFrame();
f.setTitle("ColorGrid Display Window");
f.setSize(200,200);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container contentPane = f.getContentPane();
contentPane.add(new ColorGrid());
f.show();
}
}
Any Component can have a MouseListener. JLabel is nice for a colored rectangle, as long as you make it opaque.
Addendum: Having recommended MouseAdapter elsewhere, I should mention that one instance is enough.
Addendum: This update adds the mouse listener in the ColorLabel constructor.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
/** #see http://stackoverflow.com/questions/5136859 */
public class ColorLabel extends JLabel {
private static final int N = 10;
private static final Random random = new Random();
private static final MouseAdapter listener = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
ColorLabel label = (ColorLabel) e.getSource();
label.setBackground(new Color(random.nextInt()));
}
};
public ColorLabel() {
this.setOpaque(true);
this.setBackground(new Color(random.nextInt()));
this.setPreferredSize(new Dimension(32, 32));
this.addMouseListener(listener);
}
private void displayGrid() {
JFrame f = new JFrame("ColorGrid");
f.setLayout(new GridLayout(N, N));
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 0; i < N * N; i++) {
final ColorLabel label = new ColorLabel();
f.add(label);
}
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ColorLabel().displayGrid();
}
});
}
}
Instead of having a JPanel that you draw your grid of colors on, how about you have a grid of buttons. You override the drawing mechanism for the button so that it just renders as it's current color. Then you have functionality built in to listen for clicks to occur in a specific section of your grid.
This is what I came up with.
Note: I'm still studying Java in University, so this might not be the exact way to do this but it worked when I did it.
public class ColorGrid extends JPanel implements MouseListener {
this.addMouseListener(this);
addMouseListener(this);
That's the first part, the second part is to have these methods in your code.
public void mouseClicked(MouseEvent arg0) {
}
public void mouseEntered(MouseEvent arg0) {
}
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
}
public void mouseReleased(MouseEvent arg0) {
}
Then, depending on what you want, (i.e. Mouse clicked or pressed), just type in:
repaint();
Hope this helped.
Assuming you have a 2d array of colors, you can simply use the x and y the mouselistener gives you when you click to calculate the indices of that rectangle. Just divide the x and y by the size of the rectangle using integer division. After changing the color use repaint() to show it.
Related
Using LayerUI to add labels to the upper corner of a tabbed pane. Would like to allow these labels to display as hyperlinks, so I set the color blue, the cursor to a hand and I added a mouselistener.
Howev,er when I paint the component the cursor customization and mouse listener are not not working.
sample image
Sample Application:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.LayerUI;
public class TopRightCornerLabelLayerUITest {
public static JPanel makeUI() {
JPanel resultPanel = new JPanel();
resultPanel.setLayout( new BorderLayout());
resultPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.add("Tab 1", new JPanel());
tabbedPane.add("Tab 2", new JPanel());
resultPanel.add(new JLayer<JComponent>(tabbedPane, new TopRightCornerLabelLayerUI()), BorderLayout.CENTER);
return resultPanel;
}
private static void initandShow()
{
JDialog dialog = new JDialog();
dialog.getContentPane().add(makeUI());
dialog.setSize(520, 240);
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
initandShow();
}
});
}
}
class TopRightCornerLabelLayerUI extends LayerUI<JComponent> {
private JPanel rubberStamp = new JPanel();
#Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
JLabel layoutHyperlink = new JLabel("<html><a href=''>File Layout and Descriptions</a></html>");
JLabel templateHyperlink = new JLabel("<html><a href=''>Download Template</a></html>");
layoutHyperlink.setForeground(Color.BLUE.darker());
layoutHyperlink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
layoutHyperlink.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// the user clicks on the label
System.err.println("clicked");
}
});
templateHyperlink.setForeground(Color.BLUE.darker());
templateHyperlink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
templateHyperlink.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// the user clicks on the label
System.err.println("clicked");
}
});
// Add components
Dimension templateDimension = templateHyperlink.getPreferredSize();
int x = c.getWidth() - templateDimension.width - 5;
SwingUtilities.paintComponent(g, templateHyperlink, rubberStamp, x, 2, templateDimension.width , templateDimension.height);
Dimension layoutDimension = layoutHyperlink.getPreferredSize();
x = c.getWidth() - layoutDimension.width - 15 - templateDimension.width;
SwingUtilities.paintComponent(g, layoutHyperlink, rubberStamp, x, 2, layoutDimension.width, templateDimension.height);
}
}
I was actually unaware of class JLayer until I read your question. I don't have a complete answer but I think it's enough to give you a push in the right direction. I was helped by the lesson in Oracle's Java tutorial: How to Decorate Components with the JLayer Class. That lesson has a section entitled Responding to Events which helped me to figure out how to partially solve your issue. Basically you are just painting the labels and not actually adding them as components and therefore they will not respond to mouse events. Since the labels can be considered part of the JLayer component that is added as a component, you can configure that JLayer to respond to mouse events. As stated in the tutorial lesson, you need to override some other methods in your TopRightCornerLabelLayerUI class. The code below contains two of those methods. Add them to your code and see if they give you the expected result.
public void installUI(JComponent c) {
super.installUI(c);
((JLayer<?>) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
}
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_CLICKED) {
Point pt = e.getPoint();
if (pt.x >= xTemplateHyperlink && pt.x <= (xTemplateHyperlink + widthTemplateHyperlink)) {
System.out.println("clicked");
}
}
}
EDIT:
Forgot to mention that I added the following members to your TopRightCornerLabelLayerUI class...
private int xTemplateHyperlink;
private int yTemplateHyperlink;
private int widthTemplateHyperlink;
private int heightTemplateHyperlink;
And set their values in method paint() like so...
Dimension templateDimension = templateHyperlink.getPreferredSize();
xTemplateHyperlink = c.getWidth() - templateDimension.width - 5;
yTemplateHyperlink = 2;
widthTemplateHyperlink = templateDimension.width;
heightTemplateHyperlink = templateDimension.height;
which explains the code in method processMouseEvent().
My code is basically about having a frame and it has a button. You press the button you can draw rectangles, getting coordinates from the mouse press and the mouse release.
Now, if you remove the button the code works perfectly, here is the code.
//testing file
package ActionTest;
import java.awt.*;
import javax.swing.*;
public class MouseTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new MouseFrame();
frame.setTitle("MouseTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(500,500);
}
});
}
}
My frame, calls on the mouse component
package ActionTest;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MouseFrame extends JFrame
{
public MouseFrame()
{
add(new MouseComponent());
}
}
The component class: handles mouse clicks and drawing the rectangle
package ActionTest;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
public class MouseComponent extends JComponent
{
Point first;
Point second;
private ArrayList<Rectangle2D> rectangles;
public MouseComponent()
{
rectangles = new ArrayList<>();//contains rectangles
addMouseListener(new MouseHandler());
}
//paint method of the component, simply re-paint the array
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
for (Rectangle2D r : rectangles)
g2.draw(r);
}
/**
* creates a rectangle and adds it to the rectangles ArrayList
* then repaint the component
* inside some operations to deal with rectangle drawing nothing to do with the issue
* #param p1: first coordinates
* #param p2: second coordinates
*/
public void addRec(Point2D p1, Point2D p2)
{
double w, h, x, y;
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
if(x1 <= x2){
x = x1;
w = x2-x1;
}
else{
x = x2;
w = x1-x2;
}
if (y1 <= y2){
y = y1;
h = y2-y1;
}
else{
y = y2;
h = y1-y2;
}
rectangles.add(new Rectangle2D.Double(x, y, w, h));
repaint();
}
//records mouse click and mose release
//you press the mouse that is the 1st coordinates
//you release it that is the 2nd coordinates
//pass both to the addRec method
private class MouseHandler extends MouseAdapter
{
#Override
public void mousePressed(MouseEvent event)
{
first = event.getPoint();
}
#Override
public void mouseReleased(MouseEvent event)
{
second = event.getPoint();
addRec(first, second);
}
}
}
It works perfectly. However, going back to the original problem, if I add a button, and then when the button pressed go ahead and begin drawing rectangles it doesn't work.
Here is the modified frame class
package ActionTest;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MouseFrame extends JFrame
{
private JPanel buttonPanel;
public MouseFrame()
{
JFrame frame = this;
buttonPanel = new JPanel();
JButton rec = new JButton("Rectangle");
rec.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event)
{
System.out.println("pressed");
frame.add(new MouseComponent());
}});
buttonPanel.add(rec);
add(buttonPanel);
}
}
Thanks in advance.
frame.add(new MouseComponent());
The size of a newly created component is (0, 0) so there is nothing to paint. So you need to invoke the layout manager when you add a component to a visible GUI.
frame.add(new MouseComponent());
frame.revalidate();
frame.repaint();
Note this will only work if the layout manager allows you to add multiple components to the frame. The default layout manager for a frame is the BorderLayout and only a single components can be added to the CENTER of the BorderLayout.
So maybe you need to add the button using:
frame.add(button, BorderLayout.PAGE_START);
Read the section from the Swing tutorial on How to Use Layout Managers for more information and working examples.
Also, any time you do custom painting you need to override the getPreferredSize() method of the custom component so the layout managers can do their job.
I'm creating a sort of paint application. The user can move a circle in a JPanel by pressing/dragging the mouse.
I have a JCheckBoxMenuItem in one of my JMenus:
JCheckBoxMenuItem checkitem = new JCheckBoxMenuItem("Draw mode",false);
When it is not activated, the circle can only be moved (by dragging/pressing) and the previous circle will be erased.
When it is activated, the circle can only be moved, but the previous circle will not be erased when dragging/pressing the mouse ( This works the same way as a paint program )
Shortened version of my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class GUI extends JFrame implements MouseListener, MouseMotionListener, ActionListener, ItemListener
{
JPanel mainPan, colorPan;
Color color = Color.BLACK;
JCheckBoxMenuItem checkitem;
boolean clear = true;
public GUI(String header)
{
maker();
mainPan.addMouseListener(this);
mainPan.addMouseMotionListener(this);
add(mainPan , BorderLayout.CENTER);
add(colorPan, BorderLayout.PAGE_END);
}
public void maker()
{
colorPan = new JPanel();
colorPan.setLayout(new GridLayout(1, 0));
mainPan = new JPanel(){
#Override
public void paintComponent(Graphics g)
{
//g.setColor(Color.WHITE);
//g.fillRect(0,0,getWidth(),getHeight());
if(clear)
super.paintComponent(g); //Do the same thing as above(Clear JPanel)
g.setColor(color);
g.fillOval(x,y,50,50); //x and y are integer variables that I use in my full program
}
};
checkitem = new JCheckBoxMenuItem("Draw mode",false);
//After adding this to a JMenu,
checkitem.addItemListener(this);
}
public void itemStateChanged(ItemEvent e)
{
if(e.getStateChange() == ItemEvent.SELECTED)
{
clear = false;
}
else
{
clear = true;
}
}
}
The below screenshot shows the output of my full program:
colorPan is the JPanel full of JButtons of different colors. The top of it is mainPan.
Right now, the "Draw mode" doesn't work as expected. I had always thought that super.paintComponent(g); was the one that clears/resets the screen when repaint() is called. But I removed that and was quite surprised to see the program behave the same way.
Basically, my problem is here:
if(clear)
super.paintComponent(g);
I need to prevent everything from being cleared when repaint() is called. How do I achieve what I want?
It is not in this code where changes should be made. And it is not paint method which should be changed. Paint paints whenever is required either by your or by system. When window is resized or moved or partially covered - it uses paint to paint picture again.
What you should really do is to stop updating coordinates for your painted oval. It could be done in mouse listener or in coordinates setter or, better, in control part which manages these coordinates. Your checkbox should control ability to change your model. It should not control painting.
There is commonly used pattern Model-View-Controller - look at it. Maybe it could look like overkill for such small application but even Swing itself is built on this pattern so you already follow it. Issues rise when you try to break it. So - don't.
You can't "prevent the JPanel from being updated;" paintComponent() will be called asynchronously, as required by the system. Instead, condition attributes of your view class in a way that allows your implementation of paintComponent() to render everything whenever it is called.
In the example below, the foreground color is changed with each mouse click and paintComponent() uses the revised setting. In the more elaborate example cited here, ClearAction clears the List<Node> and List<Edge> that define the graph's model. Absent a call to super.paintComponent(g), otherwise required for an opaque component, a call to fillRect() in paintComponent() cleans up any leftover selection artifacts.
public void actionPerformed(ActionEvent e) {
nodes.clear();
edges.clear();
repaint();
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see https://stackoverflow.com/a/5312702/230513 */
public class MouseDragTest extends JPanel {
private static final String TITLE = "Drag me!";
private static final Random r = new Random();
private static final int W = 640;
private static final int H = 480;
private Point textPt = new Point(W / 2, H / 2);
private Point mousePt;
private Color color = Color.black;
public MouseDragTest() {
this.setFont(new Font("Serif", Font.ITALIC + Font.BOLD, 32));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mousePt = e.getPoint();
setColor(Color.getHSBColor(r.nextFloat(), 1, 1));
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int dx = e.getX() - mousePt.x;
int dy = e.getY() - mousePt.y;
textPt.setLocation(textPt.x + dx, textPt.y + dy);
mousePt = e.getPoint();
repaint();
}
});
}
public void setColor(Color color) {
this.color = color;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
int w2 = g.getFontMetrics().stringWidth(TITLE) / 2;
g.drawString(TITLE, textPt.x - w2, textPt.y);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame(TITLE);
f.add(new MouseDragTest());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
(Beginner)
Hi, sorry for the specific question, but I'm having errors constantly ambush me out of nowhere with a program that I would expect to be quite simple.
I was planning on creating a program that would allow the user to click on JPanels with in a GridLayout in order to change their colours. Imagine a poor man's pixel art program, like the old MS Paint.
The plan was to create a JFrame set to GridLayout, of an integer width and height, and fill the grids with JPanels with a 2d array and a for loop. I would then put a MouseListener into each individual JPanel to listen for a mouseClicked, which would change the background colour of the panel clicked.
package pixelpainter;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class PixelPainter extends JPanel {
int width = 20;
int height = 20;
int pixSize = 10;
Color bGColor = Color.WHITE;
Dimension pixDim = new Dimension(pixSize,pixSize);
private JPanel panelClicked = null;
JFrame frame= new JFrame();
/**
* #param args the command line arguments
*/
public PixelPainter()
{
initGUI();
}
public void initGUI() {
frame.setLayout(new GridLayout(height, width, 0, 0));
frame.setSize((height * pixSize), (width * pixSize));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
int[][] pixGrid = new int [width][height];
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
JPanel pixel[][] = new JPanel[width][height];
frame.add(pixel[row][col]);
pixel[row][col].setBackground(bGColor);
pixel[row][col].setPreferredSize(pixDim);
pixel[row][col].setBorder(BorderFactory.createLineBorder(Color.BLACK));
pixel[row][col].addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent click)
{
JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
if (selectedPixel == null || selectedPixel == PixelPainter.this)
{
return;
}
if (selectedPixel != null)
{
selectedPixel.setBackground(Color.BLACK);
}
}
#Override
public void mousePressed(MouseEvent press)
{
}
});
}
}
}
public static void main(String[] args){
EventQueue.invokeLater(new Runnable(){
#Override
public void run(){
new PixelPainter().setVisible(true);
}
});
}
}
Ideally I would be using the 2d array JFrame when filling in the colours, but apparently they must be final or effectively final.
I rearranged your code to group like things together.
Here's the GUI I created.
I made the following changes to your code.
I had the main class implement Runnable. Since the EventQueue invokeLater method expects a Runnable, you might as well make the main class a Runnable.
I moved the JPanel creation into the createPixels method. Your methods should do one thing and do that one thing well.
The initGUI method now just creates the JFrame.
I moved the sizing integers into the new PixelPanel class. The class that extends a JPanel has to provide a preferred size. The JFrame pack method then creates a JFrame of the correct size.
In the paintComponent method of the PixelPanel class, all I do is paint. You shouldn't do anything else but paint in the paintComponent method.
I made the pixels bigger, so I could left click and right click on a pixel easier. The left click makes the pixel blue, and the right click erases the blue (makes the pixel white).
Because of the model / view / controller pattern, I pulled the mouse adapter code into its own class. Separating the concerns makes getting each part of the GUI working properly much easier.
And here's the code.
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PixelPainter implements Runnable {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new PixelPainter());
}
#Override
public void run() {
initGUI();
}
public void initGUI() {
frame = new JFrame("Pixel Art");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createPixels());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createPixels() {
int width = 30;
int height = 20;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(height, width, 0, 0));
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
PixelPanel pixelPanel = new PixelPanel();
pixelPanel.addMouseListener(new ColorListener(pixelPanel));
panel.add(pixelPanel);
}
}
return panel;
}
public class PixelPanel extends JPanel {
private static final long serialVersionUID = 8465814529701152253L;
private static final int PIXEL_SIZE = 20;
private Color backgroundColor;
public PixelPanel() {
this.backgroundColor = Color.WHITE;
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
this.setPreferredSize(new Dimension(PIXEL_SIZE, PIXEL_SIZE));
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(getBackgroundColor());
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public class ColorListener extends MouseAdapter {
private PixelPanel panel;
public ColorListener(PixelPanel panel) {
this.panel = panel;
}
#Override
public void mousePressed(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
panel.setBackgroundColor(Color.BLUE);
panel.repaint();
} else if (event.getButton() == MouseEvent.BUTTON3) {
panel.setBackgroundColor(Color.WHITE);
panel.repaint();
}
}
}
}
Your code is creating a new pixel Array inside the loop. The idea is to create the Array out side the loo and then create a new JPanel to add to the Array inside the loop.
Something like:
int[][] pixGrid = new int [width][height];
JPanel pixel[][] = new JPanel[width][height];
and
//JPanel pixel[][] = new JPanel[width][height];
pixel[row][col] = new JPanel();
Now inside the listener because you add the listener to every panel you can access the panel directly without worrying about the mouse point:
//JPanel selectedPixel = (JPanel) getComponentAt(click.getPoint());
JPanel selectedPixel = (JPanel)click.getSource();
In fact you can create a single MouseListener to add to each panel instead of creating a different listener for each panel because the above code is generic.
I have just started working with Swing and am trying to draw a button with a custom shape, a triangle in this example. I called the JButton subclass 'ShiftingButton' in the following code because of its unusual behavior. When the mouse enters its region, it is repainted with an offset from its original position. Furthermore the shifted, offset version is drawn in addition to the original position so that both the original and shifted versions appear together. That is, when I run this code, the button is shown as a triangle along the left edge of the window. Then when I run the mouse over the button, a new triangle is drawn (in addition to the old one), shifted down and to the right by about 10 pixels. Resizing the window changes the offset of the phantom button from the original.
Experimenting with mouse clicks shows that only the original, correctly-positioned button is active. The region of the offset phantom button is not active.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.awt.Polygon;
public class ShiftingButton extends JButton implements ActionListener {
private Polygon shape;
public ShiftingButton () {
initialize();
addActionListener(this);
}
protected void initialize() {
shape = new Polygon();
setSize(120, 120);
shape.addPoint(0, 0);
shape.addPoint(0, 60);
shape.addPoint(90, 0);
setMinimumSize(getSize());
setMaximumSize(getSize());
setPreferredSize(getSize());
}
// Hit detection
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
#Override
public void paintComponent (Graphics g) {
System.err.println("paintComponent()");
g.fillPolygon(shape);
}
protected void paintBorder(Graphics g) {
}
#Override
public void actionPerformed (ActionEvent ev) {
System.out.println("ShiftingButton ActionEvent!");
}
public static void main (String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
ShiftingButton button = new ShiftingButton();
panel.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
You failed to call super.paintComponent(g) inside the overridden paintComponent(...) method. Moreover, while overriding a method of the Base class, always try to keep the access specifier of the methods, the same, as much as possible. In this case it's protected and not public :-) Now function should be like this :
#Override
protected void paintComponent (Graphics g) {
System.err.println("paintComponent()");
super.paintComponent(g);
g.fillPolygon(shape);
}
EDIT 1 :
Moreover, since you are using a custom shape to be drawn, hence you again failed to specify the ContentAreaFilled property for this JButton in question, hence inside your constructor, you should write setContentAreaFilled(false), for it to work nicely. Though if this doesn't works (for reasons specified in the Docs), then you have to use the plain old Opaque property and set it to false for this JButton using setOpaque(false) :-)
Here is your code with modified changes :
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.awt.Polygon;
public class ShiftingButton extends JButton implements ActionListener {
private Polygon shape;
public ShiftingButton () {
setContentAreaFilled(false);
initialize();
addActionListener(this);
}
protected void initialize() {
shape = new Polygon();
setSize(120, 120);
shape.addPoint(0, 0);
shape.addPoint(0, 60);
shape.addPoint(90, 0);
}
#Override
public Dimension getPreferredSize() {
return (new Dimension(120, 120));
}
// Hit detection
public boolean contains(int x, int y) {
return shape.contains(x, y);
}
#Override
protected void paintComponent(Graphics g) {
System.err.println("paintComponent()");
super.paintComponent(g);
g.fillPolygon(shape);
}
protected void paintBorder(Graphics g) {
}
#Override
public void actionPerformed (ActionEvent ev) {
System.out.println("ShiftingButton ActionEvent!");
}
public static void main (String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
ShiftingButton button = new ShiftingButton();
panel.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}