I am trying to change the color of my Red Cirlces in the Action handler then repaint() and I couldn't figure out why it is not working.
Imports here
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JPanel;
My class here:
public class CirclePanel extends JPanel implements ActionListener {
static JFrame f;
static JButton run1, run2, reset, quit;
static JPanel btnPanel;
static CirclePanel circlePanel;
static final int NUM = 5;
static Color c;
static Graphics2D g2;
static Graphics2D g3;
public CirclePanel(){
f = new JFrame();
f.setTitle("Dining Philosophers");
f.setDefaultCloseOperation(EXIT_ON_CLOSE);
f.setSize(1000,1000);
f.setLayout(new BorderLayout());
btnPanel = new JPanel();
btnPanel.setPreferredSize(new Dimension(250, 100));
btnPanel.add(run1 = new JButton("Run 1"));
btnPanel.add(run2 = new JButton("Run 2"));
btnPanel.add(reset = new JButton("Reset"));
btnPanel.add(quit = new JButton("Quit"));
run1.setPreferredSize(new Dimension(180, 50));
run2.setPreferredSize(new Dimension(180, 50));
reset.setPreferredSize(new Dimension(180, 50));
quit.setPreferredSize(new Dimension(180, 50));
run1.addActionListener(this);
f.add(btnPanel, BorderLayout.SOUTH);
f.add(this, BorderLayout.CENTER);
f.setVisible(true);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g2 = (Graphics2D) g;
g3 = (Graphics2D) g;
g2.translate(470, 400);
c = Color.red;
for(int i = 0; i < NUM; ++i){
c = Color.red;
g2.setColor( c);
g2.fillOval(150, 0, 100, 100);
g3.setColor(Color.BLACK);
g3.fillOval(90, 0, 30, 30);
g2.rotate(2*Math.PI/ NUM);
}
}
As you can see when I push the button Run1 it does go into the action handler and executes the repaint method, but nothing changes.
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == run1) {
System.out.println("Entered Action Handler");
g2.setColor(Color.green);
repaint();
}
}
Here is my main:
public static void main(String[] args) {
new CirclePanel();
}
}
The Graphics objects are not long-lived, not stable, and you shouldn't be using them in this way. Instead of setting g2 or any other Graphics field, create a Color field, say called Color circleColor = ...;, and change this. Within the protected void paintComponent(Graphics g) method, call g.setColor(circleColor);, and this should work.
Delete these fields as they are dangerous:
// static Graphics2D g2;
// static Graphics2D g3;
Also your code shows gross over-use of the static modifier, and I would venture to recommend that none of your fields should be static except for the constant:
static final int NUM
The repaint() method will ultimately invoke the paintComponent() method of your panel. Swing will pass in the Graphics object to be used in the painting.
In the painting code you always hardcode the Color to be RED. Don't do this.
Instead you need to define a variable in your panel class. Lets say "circleColor" (ie, "circleColor" replaces your "c" variable because variable names should be more descriptive, not just a single character).
Then in your ActionListener code you do:
//g2.setColor(Color.green);
circleColor = Color.green;
This in the paintCompnent() method you do:
//c = Color.red;
g.setColor(circleColor);
Also:
static Graphics2D g2;
static Graphics2D g3;
There is no need for any of those variable. You always use the Graphics object that is passed to the painting method.
Read the section from the Swing tutorial on Custom Painting for more information and for better examples on how to structure your code. For example you should NOT be using static variables.
What's happening is:
You are changing g2 color in the action listener.
Then you call repaint(); so paintComponent(Graphics g) is called.
In the paintComponent(Graphics g) method you are setting the g2 color to c so you are overriding the changes you made in the action listener.
That's why the circles are not changing color in the end.
You should set c = Color.RED; in the constructor instead of in the for loop, then you can just change the value of c in the action listener c = Color.GREEN;.
Also you're setting c = Color.RED; both in the for loop and before it which is useless.
EDIT:
As suggested by #camickr your code is also badly structured.
You don't need g2 and g3 because you can draw multiple shapes
using the same Graphics g object.
In your case you don't even need to call g outside of the paintComponent method because you can simply change the Color c variable as i said above.
You also don't need all of those static variables. Just make them private and if you need to access them from outside the class you should create some getters and setters.
Here you'll find more on static variables and some examples.
Here you'll find more about getters, setters and encapsulation.
Example of how you could change your code:
public class CirclePanel extends JPanel implements ActionListener {
private JFrame f;
private JButton run1, run2, reset, quit;
private JPanel btnPanel;
private int NUM;
private Color c;
public CirclePanel(){
color1 = Color.red;
color2 = Color.black;
NUM = 5;
// Setup JFrame and stuff as you were doing.
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.translate(470, 400);
for(int i = 0; i < NUM; i++){
g.setColor(color1);
g.fillOval(150, 0, 100, 100);
g.setColor(color2);
g.fillOval(90, 0, 30, 30);
g.rotate(2*Math.PI/ NUM);
}
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == run1) {
System.out.println("Entered Action Handler");
color1 = Color.green;
repaint();
}
}
// GETTERS and SETTERS if needed
}
Related
The goal here is to use jlabels with an image icon that contains a BufferedImage. Those jlabels can then be easily moved around with the mouse without having to go searching a ton of different BufferedImages on the screen to find out which one is being clicked on.
This is easy to do in a standard JFrame. I've been searching around here for an hour trying to figure out how to implement this in a game loop where a paintComponent is overridden.
import javax.swing.*;
import java.awt.*;
public class Main {
public Main() {
}
public static void main(String[] args) {
JFrame window = new JFrame();
GamePanel gamePanel = new GamePanel();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(true);
window.setLayout(new FlowLayout());
window.setTitle("FX Test");
window.add(gamePanel);
window.pack();
window.setLocationRelativeTo(null);
window.setVisible(true);
gamePanel.startGameThread();
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
public class GamePanel extends JPanel implements ActionListener {
private Timer gameLoopTimer;
public int screenWidthPixels = 640;
public int screenHeightPixels = 480;
private int counter;
private int x = 1;
private float alpha = 1;
private final int DELAY = 15;
private final int INITIAL_DELAY = 200;
public GamePanel() {
this.setPreferredSize(new Dimension(screenWidthPixels, screenHeightPixels));
this.setBackground(Color.black);
this.setDoubleBuffered(true);
this.setFocusable(true);
this.requestFocus();
counter = 0;
JButton testButton = new JButton("Button Test");
this.add(testButton);
JLabel label = new JLabel(new String("Label test"));
label.setVisible(true); // Doesn't seem to be needed.
this.add(label);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.WHITE);
g2.drawString("Game Panel Testing: " + counter,128,129);
g2.dispose();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
update();
}
void startGameThread() {
gameLoopTimer = new Timer(DELAY,this);
gameLoopTimer.setInitialDelay(INITIAL_DELAY);
gameLoopTimer.start();
}
}
That code draws "Game Panel Testing: " and the incrementing counter, but no button and no label.
If I comment out the entire paintComponent I'm overriding, the button and label appear as expected.
What I can't wrap my head around is how to get the label and button to appear again once paintComponent is overridden. I thought the super.paintComponent(g) would take care of that automatically, but clearly I'm missing something here. How on earth can I add a bunch of JLabels to this game loop instead of having to manually handle moving of g2 drawn BufferedImages on mouse drag?
The jlabels are not drawn since you have overridden the paintComponent method.
The call to super is on the super class, so you have misunderstood how that call works.
If you put your in a class that inherits from your class with jlabels it will work.
My goal is to make a class that contains rectangle and then use it and change it in other classes.
I tried to write this code and make an object Rect rect = new Rect(); but when i start the program the rectangle doesn't show up.
I also tried to add it with window.add(rect); but had same problem i'm sure im doing something wrong but i don't really know what.
One more thing that i tried was calling method from other class Rect.drawRect(g); but then it asks for "Argument" and if i add Argument g like i had in method drawRect it says "g cannot be resolved to a variable"
I hope someone can explain and tell me what did i do wrong, also would be great to know how to make rectangle or a circle and later use it in other classes and maybe change its color and size, I only found how to do it in one class.
import javax.swing.JFrame;
public class Main extends Rect{
public static void main(String[] args ) {
JFrame window = new JFrame("test");
window.setSize(1000, 800);
window.setVisible(true);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Rect rect = new Rect();
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rect extends JPanel{
public void drawRect(Graphics g){
g.setColor(Color.RED);
g.fillRect(100, 100, 200, 200);
}
}
The most important thing is that you need to write some code to do the painting. This is done by overriding the paintComponent method inside your Rect class a bit like this:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(100, 100, 200, 200);
}
Your second issue is that you want to be able to change the colour and size of your rectangle from other classes. For a simple example, this can easily be done by adding some static values inside your Rect class:
public static Color myColor = Color.RED;
public static int myX = 100;
public static int myY = 100;
public static int myWidth = 200;
public static int myHeight = 200;
Now update your paint method to use the static values:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(myColor);
g.fillRect(myX, myY, myWidth, myHeight);
}
Now, whenever or wherever you use the Rect panel it will now show the rectangle according to the static values.
For example, below is a simple and working program, note how it uses the following:
//create Rect
Rect rect = new Rect();
//set the size of the new panel
rect.setPreferredSize(new Dimension(800, 600));
//add the rect to your JFrame
window.add(rect);
//now you can change the color for all Rect instances
//Note how I use Rect instead of rect, however, both will work.
Rect.myColor = Color.BLUE;
//And dont forget to repaint it if you want to see the changes immediatly
rect.repaint();
Full example, main class:
import javax.swing.JFrame;
import java.awt.Color;
public class Main{
//Note how we don't need to extend the Rect class (It just adds confusion)
public static void main(String[] args ) {
JFrame window = new JFrame("test");
window.setSize(1000, 800);
window.setVisible(true);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create Rect
Rect rect = new Rect();
//set the size of the new panel
rect.setPreferredSize(new Dimension(800, 600));
//add the rect to your JFrame
window.add(rect);
//now you can change the color for all Rect instances
//Note how I use Rect instead of rect, however both will work.
Rect.myColor = Color.BLUE;
//And dont forget to update it
rect.repaint();
}
}
And the Rect class:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Rect extends JPanel{
public static Color myColor = Color.RED;
public static int myX = 10;
public static int myY = 10;
public static int myWidth = 200;
public static int myHeight = 200;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(myColor);
g.fillRect(myX, myY, myWidth, myHeight);
}
}
Note, if you don't want to call Rect.repaint() every time you make a color/size change then just make a new method that changes each value and include repaint(), for example:
public void changeWidth(int width){
myWidth = width;
repaint();
}
UDP: You need override void paintComponent(Graphics g) instead void drawRect(Graphics g)and call super.paintComponent(g) inside method. Then you can use window.add(rect);.
Thanks #FredK for correction
I'd like to make a Java panel that creates objects where the user clicks. Since my actual application uses a MVC approach I'd like also for these objects to be able to repaint themselves when a model is changed, and provide menus to change their properties.
I think that the best way to control their x and y locations would be to take a canvas based approach whereby the JPanel calls a draw method on these objects from the paintComponent method. This however will only draw the shape on the canvas and does not add the object itself loosing all abilities to control object properties. I'd be very grateful if someone could tell me the best approach for what I want to do.
I've created some sample code which can be seen below. When clicked I'd like the circle to change colour, which is implemented using a MouseListener (it basically represents changing the models properties in this small example). Also I'd just like to make sure that zooming in/out still works with any sample code/advice can provide so I've added buttons to zoom the objects in and out as a quick test.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
public class Main {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ExamplePanel panel = new ExamplePanel();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
});
}
//I could not get this to with when it extended JLayeredPane
private static class ExamplePanel extends JPanel {
private static final int maxX = 500;
private static final int maxY = 500;
private static double zoom = 1;
private static final Circle circle = new Circle(100, 100);
public ExamplePanel() {
this.setPreferredSize(new Dimension(maxX, maxY));
this.setFocusable(true);
Button zoomIn = new Button("Zoom In");
zoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom += 0.1;
repaint();
}
});
add(zoomIn);
Button zoomOut = new Button("Zoom Out");
zoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
zoom -= 0.1;
repaint();
}
});
add(zoomOut);
// add(circle); // Comment back in if using JLayeredPane
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.scale(zoom, zoom);
super.paintComponent(g);
circle.paint(g); // Comment out if using JLayeredPane
}
}
static class Circle extends JPanel {
private Color color = Color.RED;
private final int x;
private final int y;
private static final int DIMENSION = 100;
public Circle(int x, int y) {
// setBounds(x, y, DIMENSION, DIMENSION);
this.x = x;
this.y = y;
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
color = Color.BLUE;
}
#Override
public void mouseReleased(MouseEvent e) {
}
});
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(color);
g2.fillOval(x, y, DIMENSION, DIMENSION);
}
// I had some trouble getting this to work with JLayeredPane even when setting the bounds
// In the constructor
// #Override
// public void paintComponent(Graphics g) {
// Graphics2D g2 = (Graphics2D) g;
// g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// g2.setPaint(color);
// g2.fillOval(x, y, DIMENSION, DIMENSION);
// }
#Override
public Dimension getPreferredSize(){
return new Dimension(DIMENSION, DIMENSION);
}
}
}
As an aside I did try using a JLayeredPane(useful because I'd also like to layer my objects) but could not get my objects to even render. I know it has no default layout manager so tried calling setBounds in the circle in the constructor, but sadly it did not work. I know it's better to use a layout manager but can't seem to find one suitable for my needs!
Thanks in advance.
Don't override paint components, use paintComponent and don't forget to call super.paintComponent
A component already has a concept of "location", so when painting, the top left position of your component is actually 0x0
What you are doing is actually painting beyond the boundaries of you component
For example, if you place your Circle at 100x100 and then did...
g2.fillOval(x, y, DIMENSION, DIMENSION);
You would actually start painting at 200x200 (100 for the actual location of the component and 100 for you additional positioning).
Instead use
g2.fillOval(x, y, DIMENSION, DIMENSION);
And go back and try using JLayeredPane.
You could actually write your own layout manager that takes the location of the component and it's preferred size and updates the components bounds and then apply this to a JLayeredPane. This gives you the "benefits" of an absolute layout, but keeps you within how Swing works to update its components when things change.
You should also be careful with doing anything like...
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
The Graphics context is a shared resource. That means, anything you apply to, will still be in effect when the next component is painted. This may produce some strange results.
Instead try using...
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//...
g2.dispose();
Updated
For zooming I would take a closer look at JXLayer (or JLayer in Java 7)
The JXLayer (and excellent PBar extensions) have gone quite on the net, so you can grab a copy from here
(I tried finding a better example, but this is the best I could do with the limited time I have available)
Updated with working zooming example
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.jdesktop.jxlayer.JXLayer;
import org.pbjar.jxlayer.demo.TransformUtils;
import org.pbjar.jxlayer.plaf.ext.transform.DefaultTransformModel;
public class TestJLayerZoom {
public static void main(String[] args) {
new TestJLayerZoom();
}
public TestJLayerZoom() {
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 JXLayer<JComponent> layer;
private DefaultTransformModel transformModel;
private JPanel content;
public TestPane() {
content = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = 0;
JLabel label = new JLabel("Hello");
JTextField field = new JTextField("World", 20);
content.add(label, gbc);
content.add(field, gbc);
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
final JSlider slider = new JSlider(50, 200);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int value = slider.getValue();
double scale = value / 100d;
transformModel.setScale(scale);
}
});
content.add(slider, gbc);
transformModel = new DefaultTransformModel();
transformModel.setScaleToPreferredSize(true);
Map<RenderingHints.Key, Object> hints = new HashMap<>();
//hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
//hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
layer = TransformUtils.createTransformJXLayer(content, transformModel, hints);
setLayout(new BorderLayout());
add(layer);
}
}
}
I've left the rendering hints in to demonstrate their use, but I found that they screwed with the positing of the cursor within the text field, but you might like to have a play
I'd just like to add that I fixed the zooming issue not in the way suggested by the answer, but just by keeping the line that applied a scaled transform call in the ExamplePanel paintComponent method:
g2.scale(zoom, zoom);
I thought that this was the nicest implementation since none of the components require any knowledge about zooming and it seemed far simpler than JLayer since I only required basic zooming functionalities.
I have created a simple GUI which contains a circle filled with a random colour. I am now trying to make it so that the colour of the circle changes when the mouse is clicked to another random colour. I have created a paint component method which initially paints the circle and a repaint method which will change the colour of the circle. I have then called upon this method in my mouse listener event class. The problem is that I am getting an error when I add my action listener to my pane. The error is as follows:
No enclosing instance of type taskTwo is accessible. Must qualify the allocation with an enclosing instance of type taskTwo (e.g. x.new A() where x is an instance of taskTwo).
I understand why I am getting this error but do not know how to fix it, I have tried moving the action listener class into a class of its own but then I cannot call upon my repaint method within the listener. Here is my code:
package weekThree;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class taskTwo extends JComponent {
static Random rand = new Random();
JPanel pane = new JPanel();
public static void main(String[] args) {
JFrame window = new JFrame("Task Two");
JPanel pane = new JPanel();
pane.setLayout(new FlowLayout());
taskTwo t2 = new taskTwo();
window.setContentPane(t2);
t2.paint(null);
pane.addMouseListener(new MouseClick());
window.setBackground(Color.WHITE);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(300,300);
window.setVisible(true);
}
public void paintComponent(Graphics g) {
float red = rand.nextFloat();
float green = rand.nextFloat();
float blue = rand.nextFloat();
Color randomColor = new Color(red, green, blue);
g.drawOval(300, 300, 200, 200);
g.setColor(randomColor);
g.fillOval(300, 300, 200, 200);
}
public void repaint(Graphics g) {
float red = rand.nextFloat();
float green = rand.nextFloat();
float blue = rand.nextFloat();
Color randomColor = new Color(red, green, blue);
g.drawOval(300, 300, 200, 200);
g.setColor(randomColor);
g.fillOval(300, 300, 200, 200);
}
class MouseClick implements MouseListener {
public void mouseClicked(MouseEvent e) {
repaint();
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
}
}
Thanks, also an extension of this would be to make it so that the colour changes only when I click within the circle, any tips on how to do this would be appreciated.
I would add the MouseListener in the instance world, not in the static world, and that error will go away. I also recommend that you get rid of that strange repaint method as it looks too similar to Swing JComponent's own repaint method. You never call it and so it does you no good.
Also randomize your colors in the mouse listener, not in the paintComponent method (which should be protected). I also prefer to extend JPanel and not JComponent.
e.g.,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyTaskToo extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private Color circleColor = Color.RED;
private int circX = 10;
private int circY = circX;
private int circW = PREF_W - 2 * circX;
private int circH = PREF_H - 2 * circY;
public MyTaskToo() {
// add in constructor -- in the "instance realm" not in the static realm
addMouseListener(new MyMouse());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// to smooth out graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(circleColor);
g2.fillOval(circX, circY, circW, circH);
g2.setColor(Color.BLACK);
g2.drawOval(circX, circY, circW, circH);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
Random rand = new Random();
#Override
public void mousePressed(MouseEvent e) {
float red = rand.nextFloat();
float green = rand.nextFloat();
float blue = rand.nextFloat();
circleColor = new Color(red, green, blue);
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("MyTaskToo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MyTaskToo());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
The problem is that your MouseClick class is not static but you are trying to instantiate it from a static context (the main method).
You have multiple solutions:
turn your class into static class MouseClick
create and add the MouseListener in the taskTwo() constructor (since MouseClick is an internal class of taskTwo)
create a specific taskTwo to provide the requested instance, eg new taskTwo.MouseClick(), but don't do it, since it makes no sense in this situation.
The problem is that you are accessing the MouseClick class from the public static void main function. Since, MouseClick is inside taskTwo he canĀ“t access it, you first need to create an instance of taskTwo.
Quick fix : Add constructor and erase that lane from the main function.
public taskTwo (){
this.addMouseListener(new MouseClick());
}
I have two separate class and driver files, and in the class file I create the paint method:
public void paint(Graphics g){
g.drawLine(......
....
//along with all of my other draw commands
}
Further down in the code, I create a JButton and within this button's action listener I don't know how to use a Graphics object to create more graphics in the JFrame. Should I be adding something to my driver to make this happen, or is there a way to use these graphics within my action listener? Thank you, and any help is appreciated.
You need to draw everything within the paint method. The actionPerformed should only change the state of something already in the paint method, and then call repaint. For example
boolean drawHello = true;
boolean drawWorld = false;
protected void paintComponent(Graphics g) {
super.paintCompoent(g);
if (drawHello)
g.drawString("Hello", 50, 50);
if (drawWorld)
g.drawString("World", 10, 10);
}
Then in your actionPerformed, you can change the state of drawWorld to true and call repaint().
public void actionPerformed(ActionEvent e) {
drawWorld = true;
repaint();
}
So as you can see, everything should be drawn in the paintComponent method. You can just hide and paint renderings, and make them "visible" from a action command. You should already have predefined what could posibly be drawn. Then just change the state of it rendering
And as #MadPrgrammer pointed out, you should not be painting on top-level containers like JFrame. Instead paint on a custom JPanel or JComponent and override the paintComponent method, instead of JFrame and paint
Here's an example where I draw a new square every time the button is pressed. If look at the code, you will see that in the paintComponent method, I loop through a list of Squares and draw them, and in the actionPerformed all I do is add a new Square to the List and call repaint()
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class AddSquares {
private int R = 0;
private int G = 0;
private int B = 0;
private int xLoc = 0;
private int yLoc = 0;
List<Square> squares = new ArrayList<>();
private JButton addSquare = new JButton("Add Square");
private RectsPanel panel = new RectsPanel();
public AddSquares() {
addSquare.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
Color color = new Color(R, G, B);
squares.add(new Square(xLoc, yLoc, color));
panel.repaint();
R += 10;
G += 20;
B += 30;
xLoc += 20;
yLoc += 20;
}
});
JFrame frame = new JFrame("Draw Squares");
frame.add(panel, BorderLayout.CENTER);
frame.add(addSquare, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private class RectsPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Square square : squares) {
square.drawSquare(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
}
private class Square {
int x = 0;
int y = 0;
Color color;
public Square(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void drawSquare(Graphics g) {
g.setColor(color);
g.fillRect(x, y, 75 ,75);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AddSquares addSquares = new AddSquares();
}
});
}
}
It's difficult to be 100%, but it would seem as you don't understand how custom painting is performed in Swing.
Start by taking a look at Performing Custom Painting and Painting in AWT and Swing.
Essentially, painting is arranged by the Repaint Manager, which decides what and when something should be painted. It then calls (through a chain of methods) the paint method of the components it thinks need to be updated, passing it a reference to a Graphics context that should be used to actually paint on.
Basically, when ever your paint method is called, you should create paint the current state of your painting.
You should avoid overriding paint and instead use paintComponent from classes the extend JComponent
Your question is a little on the vague side as to what you are actually wondering about but generally speaking:
We don't override paint in Swing, we override paintComponent.
If you are already aware of this, you may be overriding paint because you are doing it on a JFrame and you found that JFrame does not have a paintComponent method. You shouldn't override paint on a JFrame. Instead, create a JPanel or something to put inside the frame and override paintComponent on the panel.
Question about the ActionListener.
It sounds like you are wanting to do painting outside of paintComponent in which case probably the best way is to do painting to a separate Image. Then you paint the Image on to the panel in paintComponent. You can also put an Image in a JLabel as an ImageIcon. Here is a very simple drawing program using MouseListener that demonstrates this (taken from here):
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
class PaintAnyTime {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new PaintAnyTime();
}
});
}
final BufferedImage image = (
new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB)
);
final JFrame frame = new JFrame();
final JLabel label = new JLabel(new ImageIcon(image));
final MouseAdapter drawer = new MouseAdapter() {
Graphics2D g2D;
#Override
public void mousePressed(MouseEvent me) {
g2D = image.createGraphics();
g2D.setColor(Color.BLACK);
}
#Override
public void mouseDragged(MouseEvent me) {
g2D.fillRect(me.getX(), me.getY(), 3, 3);
label.repaint();
}
#Override
public void mouseReleased(MouseEvent me) {
g2D.dispose();
g2D = null;
}
};
PaintAnyTime() {
label.setPreferredSize(
new Dimension(image.getWidth(), image.getHeight())
);
label.addMouseListener(drawer);
label.addMouseMotionListener(drawer);
frame.add(label);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
#MadProgrammer has already linked to the articles that I was going to link to.