Draw Rectangle on pressing JButton - java

I am currently working on a little game but I just encountered a problem:
I have three classes, the first one is the JFrame:
public class Game
{
public static void main(String[] args)
{
new Game().gui();
}
public void gui()
{
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
//frame.add(panel);
frame.add(new MainMenu());
frame.setSize(800, 700);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
Now I have two other classes, one is the mainMenu, currently consisting of just one JButton.
I decided to make the menu its own class because later, I want to call the menu by pressing escape, but the problem is that (for testing reasons) I want to draw an rectangle when "start" is pressed. I tried different approaches but nothing happens.
public class MainMenu extends JPanel implements ActionListener
{
GamePanel panel = new GamePanel();
public MainMenu()
{
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
public class DrawPanel extends JPanel
{
public void paint(Graphics g)
{
g.drawRect (10, 10, 200, 200);
}
}

There are several errors I found in your code:
You're not adding your DrawPanel to anything because this line
frame.add(panel);
is commented, so, that leads us to the next problem:
You're overriding paint() method instead of paintComponent() and also don't forget to call
super.paintComponent();
at the very beginning of the method. Without it, you're preventing your code to keep painting the rest of the components. See Custom painting in Swing
That still doesn't makes anything appear because you haven't declared a location for your DrawPanel and thus it's taking JFrame's default Layout Manager which is BorderLayout and it's default location when not specified is CENTER, and as you're adding new MainMenu() to it on the same position it replaces the DrawPanel panel, since only one component can exist on the same position.
Instead try and place the DrawPanel to the CENTER and the MainMenu to the SOUTH. It now should look like this:
Don't forget to place your program on the Event Dispatch Thread (EDT) by writing your main method as follows:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//Your constructor here
}
});
}
You're implementing ActionListener on MainMenu but not implementing it's methods, remove it or implement the actionPerformed method and move the code inside the b1 action listener to it. However I highly recommend you to take at Should your class implement ActionListener or use an object of an anonymous ActionListener class too
You're playing with MainMenu's JPanel visibility, instead you should try using a CardLayout or consider using JDialogs.
For example I would make a JDialog and place the JButton there, so it will open the JFrame with the Rectangle drawn in it.
Now, the code that made the above output and follows all recommendations (but #6) is:
import java.awt.BorderLayout;
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;
import javax.swing.SwingUtilities;
public class Game {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Game().gui();
}
});
}
public void gui() {
DrawPanel panel = new DrawPanel();
JFrame frame = new JFrame("Test");
frame.add(panel, BorderLayout.CENTER);
frame.add(new MainMenu(), BorderLayout.SOUTH);
frame.setSize(400, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
}
}
class MainMenu extends JPanel {
// GamePanel panel = new GamePanel();
public MainMenu() {
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JButton b1 = new JButton("Start");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipadx = 200;
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
add(b1, c);
}
}
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
}
As suggested in the comments by #MadProgrammer, you can also override the getPreferredSize() method of your DrawPanel and then call frame.pack():
Your DrawPanel class should now look like this:
class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(10, 10, 200, 200);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}

Related

How to change color of JPanel with ActionListener

I'm learning basics of Java Applet and Swings. I'm trying a simple code. I want to change the color of my panel when a button is clicked. Here's the code:
SimpleGui.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGui implements ActionListener {
JFrame frame;
JButton button;
public static void main(String[] args) {
SimpleGui gui = new SimpleGui();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("changes colour");
button.addActionListener(this);
MyPanel drawPanel = new MyPanel();
frame.getContentPane().add(BorderLayout.SOUTH,button);
frame.getContentPane().add(BorderLayout.CENTER,drawPanel);
frame.setSize(300, 300);
frame.setVisible(true);
}
//event handling method
public void actionPerformed(ActionEvent event) {
frame.repaint();
button.setText("color changed");
}
}
class MyPanel extends JPanel {
public void paintCompenent(Graphics g) {
g.setColor(Color.green);
g.fillRect(20, 50, 100, 100);
}
}
I added some println statements to debug and I found out that paintComponent method is not called. Can you please correct me. Where I am making mistake. Is my entire implementation is wrong?
paintComponent must be protected (see here).
change your code to :
class MyPanel extends JPanel {
protected void paintComponent(Graphics g) {
g.setColor(Color.green);
g.fillRect(20, 50, 100, 100);
}
}
Result:

Update JLabel text

I'm working on a simple GUI. On Button press i want to increase/decrease a variable and update the corresponding JLabel.
class JFrameSetUp
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class JFrameSetUp extends JFrame implements ActionListener {
private int RecHeight = 0;
private int RecWidth = 0;
//Here Buttons
JButton HeightIncrease = new JButton("+");
JButton HeightDecrease = new JButton("-");
JLabel height = new JLabel(Integer.toString(RecHeight));
JLabel width = new JLabel(Integer.toString(RecWidth));
GridLayout gridLayout = new GridLayout(2, 4);
public JFrameSetUp(){
}
public void addComponentsToPane(final Container pane){
//Create GridPanel and set Layout
JPanel grid = new JPanel();
grid.setLayout(gridLayout);
//Create buttondrawPanel and set Layout
JPanel buttondraw = new JPanel();
buttondraw.setLayout(new GridLayout(2, 0));
//Adding Components to GridPanel
//Adding Layouts to pane
pane.add(grid, BorderLayout.NORTH);
pane.add(new JSeparator(), BorderLayout.CENTER);
pane.add(buttondraw, BorderLayout.SOUTH);
}
#Override
public void actionPerformed(ActionEvent e) {
//Setting up ActionListener to Buttons
if (e.getSource() == this.HeightDecrease) {
RecHeight -= 1;
height.setText(Integer.toString(RecHeight));
} else if (e.getSource() == this.HeightIncrease) {
RecHeight += 1;
height.setText(Integer.toString(RecHeight));
}
}
}
Class with MainMethod
import javax.swing.JFrame;
public class Program {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrameSetUp frame = new JFrameSetUp();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set up the content pane.
frame.addComponentsToPane(frame.getContentPane());
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
I'm aware, that's kind a newbish question. I think I'm wrong with my Code Structure. Any help is appreciated.
Thanks in advance.
You never register any ActionListeners to the buttons...
HeightIncrease.addActionListener(this);
HeightDecrease.addActionListener(this);
You also never add the buttons to the GUI
buttondraw.add(HeightIncrease);
buttondraw.add(HeightDecrease);
You also never add the labels to the GUI either...
grid.add(height);
grid.add(width);
I reworked the code, because your example was messing with my mind, hope you don't mind...
It's conceptually the same idea, just done slightly more efficently
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int recHeight = 0;
private int recWidth = 0;
//Here Buttons
JButton heightIncrease = new JButton("+");
JButton heightDecrease = new JButton("-");
JLabel height = new JLabel(Integer.toString(recHeight));
JLabel width = new JLabel(Integer.toString(recWidth));
GridLayout gridLayout = new GridLayout(2, 4);
public TestPane() {
setLayout(new BorderLayout());
//Create GridPanel and set Layout
JPanel grid = new JPanel();
grid.setLayout(gridLayout);
grid.add(height);
grid.add(width);
//Create buttondrawPanel and set Layout
JPanel buttondraw = new JPanel();
buttondraw.setLayout(new GridLayout(2, 0));
heightIncrease.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
recHeight += 1;
height.setText(Integer.toString(recHeight));
}
});
heightDecrease.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
recHeight -= 1;
height.setText(Integer.toString(recHeight));
}
});
buttondraw.add(heightIncrease);
buttondraw.add(heightDecrease);
//Adding Components to GridPanel
//Adding Layouts to pane
add(grid, BorderLayout.NORTH);
add(new JSeparator(), BorderLayout.CENTER);
add(buttondraw, BorderLayout.SOUTH);
}
}
}
I would encourage you to spend some time having a look at How to Use Buttons, Check Boxes, and Radio Buttons and How to Write an Action Listeners for more details
After changing the value call
frame.repaint();
Good to see you learning Java! A few things I should point out.
Firstly, your variable names are good, but they don't follow the Java naming convention. Even though it seems small, it's just good practice to follow.
Of course, your actual problem; the action listener you've implemented is on the JFrame. (See how you extend JFrame and implement ActionListener?) This ActionListener should be on the button. You'll can do this a few ways.
Method 1: By adding it inline with your code
JButton heightButton = new JButton("Increase Height");
heightButton.addActionListener(new ActionListener(){
#Override
public void run(){
//run method here
}
});
Method 2: Create a class which implements ActionListener
class ButtonListener implements ActionListener{
#Override
public void run(){
//actionListener code here
}
}
And then instantiate an object of this type and add it directly to your code.
ActionListner buttonListener = new ButtonListener(); //or ButtonListener buttonListener = new ButtonListener();
JButton heightButton = new JButton("Increase Height");
heightButton.addActionListener(buttonListener);
Of course, as in MadProgrammers answer, don't forget to add the labels and such to your JFrame or JPanel. Good luck learning Java!
I bet that your program just shows nothing, isn't it? That's because in addComponentsToPane method, you didn't add any component but empty JPanels. After the comment //Adding Components to GridPanel, you should:
buttondraw.add(HeightIncrease);
buttondraw.add(HeightDecrease);
grid.add(height);
grid.add(width);
Then, to listen to button event, you should also add :
HeightIncrease.addActionListener(this);
HeightDecrease.addActionListener(this);
"this" is because your frame JFrameSetUp implements ActionListener, so when either bootton is clicked the method actionPerformed is invoked.
As JLabel.setText method will repaint itself and consequently its component hierarchi is repainted as well, you haven't to do anything othr.

Java: Why validate don't work

I want to change the panel dynamically and as this answer show, they recommend to use the cardLayout. But I want to change the whole UI(no old button left) and cardLayout seems not so convenient. So I have the following code:
JFrame frame = new JFrame ("Key test");
MyDrawPanel1 dp1 = new MyDrawPanel1(frame);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
JPanel p = new JPanel ();
p.setLayout(new BorderLayout());
p.add(dp1,BorderLayout.CENTER);
frame.getContentPane().add(p);
frame.pack();
frame.setVisible (true);
And in the MyDrawPanel1 there is a button to change panel:
public MyDrawPanel1(final JFrame frame) {
clickButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
frame.getContentPane().removeAll();
//frame.validate();
frame.getContentPane().add(new MyDrawPanel2());
((JPanel)frame.getContentPane()).revalidate();
}
});
this.add(clickButton);
}
Edit:some more codes in MyDrawPanel1.
JButton clickButton = new JButton("click");
Image image = new ImageIcon("D:/0.jpg").getImage();
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.drawImage(image, 3, 40, null);
}
public Dimension getPreferredSize() {
if (image != null) {
return new Dimension(image.getWidth(null), image.getHeight(null));
}
return super.getPreferredSize(); // default
}
But the first panel doesn't disappear and I have to minimize it to refresh it so I can see the second panel. My question is the why validate don't work and if there is any other alternatives. Thanks.
Edit:here are the pictures I snipped about the panel.(first panel):
(after clicked):
Edit:
The madProgrammer and Adarsh Singhal provide two ways to solve the problem. The first is the second the panel don't call the super.paintComponent(), so add it then it works fine (as this answer say, it is the eraser so the first panel was gone). The second is calling the frame.repaint(), but I don't understand why?
We've to use repaint() to tell the components to repaint themselves. Visualize it as your case. While revalidate is used to update the layouts. So, whenever you add/remove components dynamically, you need to call both of them.The following written code displays a JFrame set to CardLayout to draw Red dp1(JPanel) completely on JFrame. On dp1, there is a Jbutton. If you click that button, dp1 will be removed & dp2(JPanel) will be drawn. dp2 is Green to distinguish changes. It seems you've forgotten repaint().
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyFrame extends JFrame{
JButton button;
private static MyFrame frame;
public MyFrame(){
this.setSize(400, 400);
this.setLayout(new CardLayout());
this.setLocationRelativeTo(null);
JPanel dp1 = new JPanel();
dp1.setBackground(Color.RED);
add(dp1);
button = new JButton("Click me to remove dp1 & draw dp2");
dp1.add (button);
JPanel dp2 = new JPanel ();
dp2.setBackground(Color.GREEN);
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
frame.getContentPane().remove(dp1);
frame.add(dp2);
frame.revalidate();
frame.repaint();
}});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible (true);
}
public static void main(String[] args) {
frame = new MyFrame();
}
}

Class - Graphics - drawString()

I'm trying to display a message in a JPanel.
I've used the drawString() function of the Graphics class.
Here's my code :
public class Frame {
JFrame frame;
JPanel panel;
Graphics graph;
Frame() {
frame = new JFrame();
panel = new JPanel();
frame.setTitle("My wonderful window");
frame.setSize(800, 600);
frame.ContentPane(panel);
frame.setVisible(true);
}
void displayMessage(String message) {
graph = new Graphics();
graph.drawString(message, 10, 20);
}
}
I've this error :
error: Graphics is abstract; cannot be instantiated
Override the JPanel's paintComponent(Graphics g) method. IN the method you have access to a valid Graphics instance. The method called on each paint.
But may be it's better to add a JLabel to the panel. The label initially has no text and when you have a message just call setText(messageText) of the label.
You should create subclasses for your JFrame and JPanel, and override the methods you want. You could try something like:
package test;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Frame extends JFrame {
public static final String message = "HELLO WORLD!!!";
public class Panel extends JPanel {
public void paintComponent(Graphics graph) {
graph.drawString(message, 10, 20);
}
}
public Frame() {
Panel panel = new Panel();
this.setTitle("My wonderful window");
this.setSize(800, 600);
this.setContentPane(panel);
this.setVisible(true);
}
public static void main(String[] args) {
new Frame();
}
}
Also, there are a lot of great books/tutorials about this. You should read one.
Edit:
You should also read about all the JComponents (JButtons, JLabels...). They're rather useful.

JPanel is not displaying in my JFrame

so I'm playing with JPanels and JFrames and I'm noticing that the JPanel I created is not showing displaying when I add it to a Jframe object. Note, that when I created a JPanel in my Jframe constructor giving the jpanel parameters before being added to the Jframe, it worked. However now I'm using a JPanel object I created and it's not working anymore.
This is what I have done.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyGui extends JFrame {
MyMouseListener listen = new MyMouseListener();
public MyGui() {
setSize(500, 500);
//setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
getContentPane().setBackground(Color.WHITE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Panel panel = new Panel();
add(panel, BorderLayout.WEST);
//setVisible(true);
show();
}
public static void main(String[] a) {
MyGui gui = new MyGui();
}
}
class Panel extends JPanel {
MyMouseListener listen = new MyMouseListener();
public Panel() {
setPreferredSize(new Dimension(300, 300));
addMouseListener(listen);
setBackground(Color.YELLOW);
setLayout(new GridLayout(3, 1));
}
public void paint(Graphics g) {
super.paintComponents(g);
g.drawOval((int) Math.round(listen.p.getX()),
(int) Math.round(listen.p.getX()), 1, 1);
}
}
class MyMouseListener implements MouseListener {
Point p = new Point();
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse was clicked");
}
#Override
public void mousePressed(MouseEvent e) {
p = e.getPoint();
System.out.println(p);
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
EDIT:
Actually I think I've found the error. The JPanel has it's paint method which when deleted allows the Jframe to show the panel. However I need to be able to draw stuff on the JPanel.
its
super.paintComponent(g);
Advice:
1)You are making things unnecessarily complex.
e.g to close the window you should use
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
instead of the call to System.exit(0); and using window listeners
2)As said by #mKorbel , you should use SwingUtilities.invokeLater to start your gui as Java GUIs are supposed to run on EDT(Event Dispatch Thread) and should not run on main thread.
1) super.paintComponents(g); inside paint() could be
public void paintComponent(Graphics g) {
super.paintComponent(g);
....
}
2) don't to set any size setSize(500,500); or setPreferredSize(new Dimension(300, 300));, to use pack() and then (uncomment) setVisible(true) for JFrame and to override getPreferredSize() for JPanel
3) MyGui gui=new MyGui(); inside public static void main(String []a){, should be wrapped into invokeLater, more see in Oracle tutorial Initial Thread
Did you try to set the layout manager and add the panel to the contentPane instead of the JFrame itself ?
getContentPane().setLayout(new BorderLayout());
getContentPane().add(panel, BorderLayout.WEST);
Default Layout manager for frame is FlowLayout not BorderLayout. Try to setLayout(new BorderLayout()) in your MyGui contructor.
You didn't set a Layout to your content pane. Try something like getContentPane.setLayout(new Layout())
View the oracle docs for details about layout managers: http://docs.oracle.com/javase/tutorial/uiswing/layout/layoutlist.html
Hope this helps

Categories