Using repaint() method properly - java

I have a button that when pressed calls a changecolor() method in a different class where some drawing is done. The button listener works fine and I see from some logging that the color was in fact changed but my drawing is not updated. This is my current implementation:
(This method is called when a button is clicked)
public void changeWarningLightColor(){
System.out.println("change color method called");
if(warningLights.equals(Color.GREEN)){
warningLights=Color.RED;
System.out.println(warningLights);
repaint();
}
else{
warningLights=Color.GREEN;
repaint();
}
}
and my drawing is created in the same file in the above method as follows:
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawSomething();
//draw a bunch of lines
}
My question is what is the proper way to call repaint() in order to actually update the drawing? Do I need to somehow call g.repaint() or do something different?
Separate class where the frame is created:
public class MainWindow extends JFrame {
public MainWindow(){
JPanel Window = new JPanel();
JPanel LeftSidePanel = new JPanel();
LeftSidePanel.setLayout(new BorderLayout());
LeftSidePanel.add(new DrawStatus(),BorderLayout.CENTER); //where the drawing is added
Window.setLayout(new BoxLayout(Window,0));
Window.add(LeftSidePanel);
add(Window);
}
public static void main(String[] args) {
//main method for showing the frame we created with the panels, and circles inside it
MainWindow frame = new MainWindow();
frame.setSize((int) (.75*Toolkit.getDefaultToolkit().getScreenSize().getWidth()),(int) (.75*Toolkit.getDefaultToolkit().getScreenSize().getHeight()));
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setTitle("DVPWS v1.0");
frame.setResizable(false);
MenuBar menu = new MenuBar();
frame.setJMenuBar(menu);
frame.setVisible(true);
}
}

If you are using a Jframe (most likely are) do
yourFrame.repaint();
Optionally
yourPanel.repaint();

In this case you could do
public MainWindow mw = new MainWindow();
mw.repaint();
If that doesnt work(i had a similar problem) than your going to have to make an instance of JFrame instead of extending it.

Related

Why doesn't the image paint over my JPanel?

I have been struggling with this for some time. At first, I only used ActionListener, then I added the paintComponent, but I have no idea what to put there. I read some tutorials and used their code as an example, but it still doesn't work. Right now, the end result is the same as it was without PaintComponent.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Scream extends JPanel {
private JButton button = new JButton("OK");
private Color screenColor;
private JPanel panel = new JPanel();
private JFrame frame;
private Dimension screenSize;
private ImageIcon image;
private JLabel label = new JLabel(image);
private int x;
private int y;
private boolean mouseClicked;
public Scream() {
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e ) {
if (e.getSource() == button) {
mouseClicked = true;
frame.getContentPane().add(label);
frame.setSize(image.getIconWidth(), image.getIconHeight());
panel.repaint();
}
}
});
frame = new JFrame ("Existential angst");
screenColor = new Color(150, 100, 0);
panel.setBackground( screenColor );
frame.add(button, BorderLayout.PAGE_END);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1300, 700);
frame.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
image.paintComponent(this, g, 1300, 700);
}
public static void main (String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Scream scream = new Scream();
}
});
}
}
If you are trying to dynamically add an image to a panel then you need to add the label to the panel. There is no need for any custom painting.
The basic code for adding components to a visible GUI is:
panel.add(...);
panel.revalidate();
panel.repaint();
Also, don't attempt to set the size of the frame to the size of the image. A frame contains a titlebar and borders. Instead you can use frame.pack();
I noticed a couple of issues:
image is never initialized to anything so it is null, effectively making the label empty. I assume maybe your example was just incomplete?
Once I initialized the image to something, your example still did not work. Turns out adding label without specifying any constraint basically does nothing (I assume since adding a component to a border layout without a constraint puts it in the center where panel already is). When I added the label to BorderLayout.NORTH, everything worked (though resizing the frame to the size of the image makes it only partially visible since the frame includes the OK button)

repaint() not working when called from mouse listener method

I am making a Solitaire program as a side project and I am having problems with the paint window I have made.
In the program, I am having a line start at one point that ends at the position of my mouse click. When I click on the window, it successfully reads my clicks and changes the xcor and ycor variables to my mouse click position, but fails to repaint a line using the new coordinates.
public class Game_Play extends JFrame {
public int xcor = 0;
public int ycor = 0;
public void setup() { //sets up JFrame
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(0, 0);
frame.setTitle("Circles");
frame.add(new MouseHandler());
frame.addMouseListener(new MouseHandler());
frame.addMouseMotionListener(new MouseHandler());
frame.setVisible(true);
}
//listener and painting subclass
class MouseHandler extends JPanel implements MouseListener, MouseMotionListener {
//when mouse pressed, the Xcor and Ycor
//will be changed to the current mouse
//x and y cords, then it will call
//repaint() to repaint the line using the
//new Xcor and Ycor locations
public void mousePressed(MouseEvent me) {
System.out.println("mouse pressed");
xcor = me.getX();
ycor = me.getY();
//prints out new cords
System.out.println(xcor + " xcor");
System.out.println(ycor + " ycor");
repaint();
}
public void mouseReleased(MouseEvent me) { //tests to make sure listener is working
System.out.println("mouse released");
}
public void mouseClicked(MouseEvent me) {}
public void mouseEntered(MouseEvent me) {}
public void mouseMoved(MouseEvent me) {}
public void mouseExited(MouseEvent me) {}
public void mouseDragged(MouseEvent me) {}
//paints the line with the Xcor and Ycor values
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("repaint check"); //test to see if repaint has been called
g.drawLine(100, 100, xcor, ycor);
}
}
}
Note: repaint() is being called from the MouseListener method mousePressed, I also have tried calling it from different MouseListener and MouseMotionListener methods to no avail.
Note: The paintComponent method notifies me if it has been called successfully, and when I click, the paintComponent method does not execute.
Note: I did notice that if I click on the screen to set the new cords then hit the maximize button on the window, it will successfully call the repaint method with a redrawn line using the new cords.
Note: the setup() method is being called from another class in another file, the code is as follows:
public static void main(String[] args) throws IOException {
deck_Create();
deck_Shuffle();
game_setup();
BufferedImage_array_Setup();
//being called here
Game_Play a = new Game_Play();
a.setup();
//
}
Last Note: I have searched high and low for the fix to this problem, only coming up with similar problems that didn't help me. Any Feedback given is greatly appreciated.
If there are any questions, let me know and I will address them for you in a few.
Thanks!
Some comment on your code:
public void setup() {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(0, 0);
frame.setTitle("Circles");
frame.add(new MouseHandler());// your panel
frame.addMouseListener(new MouseHandler()); // your listener, also a panel, but not the one you added to your frame
frame.addMouseMotionListener(new MouseHandler()); // yet another listener, also not the panel you added to your frame
frame.setVisible(true);
}
You probably meant to write:
public void setup() {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(0, 0);
frame.setTitle("Circles");
JPanel p = new MouseHandler();
frame.add(p);
frame.addMouseListener(p);
frame.addMouseMotionListener(p);
frame.setVisible(true);
}
Note that having your UI components implement listener interfaces is not a good idea. What if you want to have two mouse listeners for different components in the panel? You can't have both listeners on the panel.
A better way is to have the listener interfaces implemented by anonymous classes, following the seperation of concerns guideline.
Another thing is to add the listeners to the components that should handle them. You should be registering these listeners on the panel, not the frame containing the panel.
And finally, you should be setting the panel as the content pane using setContentPane. Usually it's best to have the panel dictate what its size should be by overriding setPreferredSize. In that case you don't need to set the size of the containing frame, rather you call pack to size the frame to the preferred size of its subcomponents.

Can we use Paint Component without extending JPanel Class?

I'm making a gui in which draw a line using paint component method i know i can do that with extends JPanel. But i really want to know how to do this without extending anything.Like in this program
public class Main {
static JPanel panel;
public static void main(String[] args) {
JFrame frame = new JFrame("Our Frame");
panel = new JPanel();
frame.setSize(600,480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.add(panel);
}
}
And on this Way
public class Main {
public static void main(String[] args) {
Frame obj = new Frame();
}
}
The frame
public class Frame extends JFrame{
private JPanel panel;
public Frame(){
panel = new JPanel();
setSize(600,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
add(panel);
}
}
You can do something like this:
JPanel panel = new JPanel(true) {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// do painting
}
}
Well, technically you can use component.getGraphics(); to get the graphics context for a given component, then draw on that. But since the component will also repaint itself, your drawing will vanish as soon as the repaint occurs. So don't even think about doing that, you will only waste your time!
However if you have a component that renders a BufferedImage in paintComponent(), you can draw on the image (with img.getGraphics(); etc.) and the component will then reflect those drawings. This will allow you to have a simple MyPanel class, where paintComponent() will only draw the image. You can then get a reference to the image, and draw on that from whereever you want.
You're already extending something in your code, and in your case it's completely unnecessary. You can remove your Frame class and display a JFrame with the following code.
JFrame f = new JFrame();
f.setSize(600, 480);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.add(panel);
f.setVisible(true);

Drawing a graphical object

I'd like to draw something in JFrame, so i decided to use below code but although it works truly but i don't want to have any ActionListener.
public class Draw
{
public static void main(String[] args)
{
final JPanel jPanel = new JPanel();
JFrame jFrame = new JFrame("Drawing Window");
JButton jButton = new JButton("draw");
jPanel.add(jButton);
jFrame.add(jPanel);
jFrame.setBounds(0, 0, 500, 500);
// first place
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// second place
jButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
Graphics graphics = jPanel.getGraphics();
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
}
});
}
}
as you can see i added the below code inside of ActionPerfomed method:
Graphics graphics = jPanel.getGraphics();
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
now i want to put it in the first place ( the comment place in the code ) but i will get an error, and if i put it in the second place, i will get no error but it does not draw any things.
It seems that it is necessary to put the drawing methods inside actionPerformed, my question is why? and is there any other way?
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
These statements need to be moved to an overridden paintComponent(Graphics) method of the jPanel.
The getPreferredSize() method of the panel should also be overridden to return a dimension of 500x500, then, instead of:
jFrame.setBounds(0, 0, 500, 500);
Simply call:
jFrame.pack();
One other option for you is to make the method(paintComponent or paint):
public void paintComponent(Graphics graphics){
graphics.drawLine(0,0,100,100);
graphics.drawOval(0,0,100,100);
}
and call the repaint from actionListener as shown..
public void actionPerformed(ActionEvent action){
repaint();
}
If it is usefull for you you can transform it as you want....

Click button and get draw rectangle

I am new to Java....I studied that we can add two things on frame... I added button and in response by clicking on button I want rectangle as output....but i don't understand that..Why i am not getting output.....
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class customizedgui5 implements ActionListener {
JButton button;
JFrame frame;
public static void main(String[] args) {
customizedgui5 hi = new customizedgui5();
hi.go();
}
public void go() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("click me");
button.addActionListener(this);
myclass a = new myclass();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(a, BorderLayout.SOUTH);
frame.setSize(100, 100);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
frame.revalidate();
}
}
class myclass extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
I would start by taking a look at Performing Custom Painting.
The main problem in your code is you are getting NullPointerException when you click the button because the reference of frame is null.
It is null because you've shadowed it in the constructor (basically, declared another variable of the same name within the constructor)...
// I'm null..
JFrame frame;
public void go() {
// Not the same as frame above...
JFrame frame = new JFrame();
You are also going to not see any changes because of a number of reasons...
The myclass panel has no size. With BorderLayout, this won't be "too" much of a problem, but...
You've drawing outside of the visible range of the panel. The rectangle you are painting won't appear because it is being painted outside of the width and height of the panel.
The rectangle will appear before you press the button as paintComponent will be called to update the state of the panel once it's made visible on the screen...
The first thing you need to is provide some kind of size hints to the BorderLayout. Try adding...
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
To myclass.
You also don't need to repaint the frame, what you really want to repaint is the instance of myclass. Try updating customizedgui5 so that a becomes a instance variable (like frame...
//...
myclass a;
//...
public void go() {
//...
a = new myclass();
//...
}
public void actionPerformed(ActionEvent event) {
a.repaint();
}
Now, the rectangle will still be shown the moment that the panel is made visible on the screen. Sure you could try setting it invisible, but this will affect the layout of the frame, hiding your component to start with, so, instead, we need some kind of flag we can trip so we know when to paint the rectangle. This is easily achieved by using a simple boolean variable, for example...
class myclass extends JPanel {
private boolean paintRect;
public void setPaintRect(boolean paint) {
paintRect = paint;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 150);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (paintRect) {
g.setColor(Color.orange);
g.fillRect(20, 50, 100, 100);
}
}
}
Then in you actionPerformed method, you just need to set the flag...
public void actionPerformed(ActionEvent event) {
a.setPaintRect(true);
}
You may also want to take a read through Code Conventions for the Java Programming Language. It will make it easier for people to read your code...
When you click on your button, you're calling the method actionPerformed(ActionEvent event)
Take a look at what you did there. Currently, you repaint and re-validate the frame. If you want to add a rectangle to your frame, you need to do so by adding a new component to the frame that will draw the rectangle.
You could add another instance of your myclass JPanel which paints a rectangle like so:
public void actionPerformed(ActionEvent event) {
frame.getContentPane().add(new myclass(), BorderLayout.NORTH);
frame.repaint();
}
This would add your custom rectangle-drawing panel to the North section of your BorderLayout. If you want to add the rectangle "on top of" your button, you should embed your button within a JPanel, then add the rectangle-drawing panel to that instead of your main JFrame

Categories