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
Related
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);
}
}
I have written a custom glasspane component and set that to my JFrame and JDialog classes.
I don't intercept mouse events in the custom glasspane, as I don't need to and also because there are a number of components on the GUI e.g. trees, popups etc which makes it complicated trying to intercept and forward mouse events.
Everything works fine as is - except that whenever I call getMousePosition() on any of the other components (e.g. JPanel) in the frame/dialog it returns null.
On my JFrame and JDialog classes, I have set the glasspane to disabled (but visible) and it works for the most part. What do I need to do so the getMousePosition() method returns the correct value without having to add mouse event listeners to the glasspane component.
Sample code that demonstrates the problem.
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.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GlassPaneExample
{
private JPanel panel;
private JButton btn;
private JLabel response;
private int counter = 0;
GlassPaneExample()
{
buildUI();
}
private void buildUI()
{
JFrame frame = new JFrame("GlassPane Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getPanel());
frame.setGlassPane(new MyGlassPane());
frame.getGlassPane().setVisible(true);
frame.getGlassPane().setEnabled(false);
frame.setPreferredSize(new Dimension(200, 220));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
JPanel getPanel()
{
panel = new JPanel(new BorderLayout());
panel.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e)
{
System.out.println("mousePosition : " + panel.getMousePosition());
System.out.println("mousePosition(true) : " + panel.getMousePosition(true));
}
});
btn = new JButton("Click here...");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
response.setText("Button click number " + getButtonClickCount());
}
});
response = new JLabel();
response.setToolTipText("Response label");
panel.add(btn, BorderLayout.NORTH);
panel.add(response, BorderLayout.SOUTH);
return panel;
}
private int getButtonClickCount()
{
return counter++;
}
public static void main(String[] arg)
{
new GlassPaneExample();
}
class MyGlassPane extends JComponent
{
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(10,40,120,120);
}
}
}
If you comment out the 3 lines to do with adding the glasspane to the Frame, then the mouse position point is printed out correctly.
This is a simple example to illustrate the problem, and I've said above, I dont want to add mouse listeners to the custom glasspane component as it introduces other complications.
I am dealing with CardLayout. The JPanel I added as a contentpane to my JFrame has a CardLayout, and I want to swap between different panes. I have a working pane with buttons and five other image panes for the tutorial that are to be displayed only if a certain boolean value is true. I mean, every time this boolean is set true, five swaps should be done using next() method. My problem is that, after the first swap, the screen becomes blank. why does this happen?
Second question. I am using a MouseListener to swap, but I would like the program to do it automatically after some time. I tried to use Thread.sleep(5000), but I get a black screen.
This is my code, where card is a class variable in order to use it in the Mouselistener, parent is the working panel, already created, and ImagePanel is a class to create tutorialPanels, which adds to them the MouseListener below. Also, rootPane is a class pane.
card = new CardLayout();
rootPane = new JPanel(card);
this.getContentPane().add(rootPane);
//create panels to add
ImagePanel inputTutorial = new ImagePanel("backgroundIn.png");
ImagePanel numericTutorial = new ImagePanel("backgroundNum");
ImagePanel outputTutorial = new ImagePanel("backgroundOut");
ImagePanel commandTutorial = new ImagePanel("backgroundCom");
ImagePanel errorTutorial = new ImagePanel("backgroundErr");
ImagePanel finalTutorial = new ImagePanel("backgroundFinal");
//add the panels
rootPane.add(parent);
rootPane.add(inputTutorial);
rootPane.add(numericTutorial);
rootPane.add(outputTutorial);
rootPane.add(commandTutorial);
rootPane.add(errorTutorial);
rootPane.add(finalTutorial);
//set rootPane content panel
this.getContentPane().add(rootPane);
//if the boolean is true
if (firstTime == true) {
card.next(rootPane);
//other swaps done by mouselisteners
}
This is the mouselistener:
//mouse click listener
private class MouseActionListener implements MouseListener {
public void mousePressed (MouseEvent e) {
}
#Override
public void mouseClicked(MouseEvent arg0) {
card.next(rootPane);
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
I know that the listener is executed because I checked it.
Any help would be appreciated, thank you in advance.
"but I would like the program to do it automatically after some time. I tried to use Thread.sleep(5000)"
Don't use Thread.sleep. Instead use a javax.swing.Timer. You can learn more at How to Use Swing Timers
Here's a simple example, using some of your app format.
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class SlideShow {
public SlideShow() {
final CardLayout layout = new CardLayout();
final JPanel mainPanel = createMainPanel(layout);
Timer timer = new Timer(1000, new ActionListener(){
public void actionPerformed(ActionEvent e) {
layout.next(mainPanel);
}
});
timer.start();
JFrame frame = new JFrame();
frame.add(mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CardLayout layout) {
JPanel panel = new JPanel(layout);
panel.add(new ImagePanel("mario.png"));
panel.add(new ImagePanel("bowser.png"));
panel.add(new ImagePanel("luigi.png"));
panel.add(new ImagePanel("koopa.png"));
panel.add(new ImagePanel("princess.png"));
return panel;
}
private class ImagePanel extends JPanel {
BufferedImage image;
public ImagePanel(String fileName) {
try {
image = ImageIO.read(getClass().getResource("/marioblobs/" + fileName));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(200, 200)
: new Dimension(image.getWidth(), image.getHeight());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SlideShow();
}
});
}
}
How to resize popup component after invoke show() method?
This example is not work:
package test;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class MyFrame extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyFrame().setVisible(true);
}
});
}
public MyFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize(400, 300);
setLocation(400, 300);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
showPopup();
}
});
}
void showPopup() {
JComponent popup = new JPanel();
popup.setPreferredSize(new Dimension(100, 40));
popup.setBorder(BorderFactory.createLineBorder(Color.GREEN));
Popup p = PopupFactory.getSharedInstance().getPopup(this, popup, this.getLocationOnScreen().x, this.getLocationOnScreen().y);
p.show();
popup.setPreferredSize(new Dimension(200, 80));
popup.setBorder(BorderFactory.createLineBorder(Color.RED));
}
}
Size of the component popup does not change.
I found the solution, you must explicitly change size of the window of popup component.
SwingUtilities.getWindowAncestor(popup).setSize(200, 80);
But this solution works only in case if popup component located in another window. And this is not what I need.
Don't provide the preferred size. It will re size automatically.
popup.setPreferredSize(new Dimension(200, 80));
In the below example, how can I get the JPanel to take up all of the JFrame? I set the preferred size to 800x420 but it only actually fills 792x391.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BSTest extends JFrame {
BufferStrategy bs;
DrawPanel panel = new DrawPanel();
public BSTest() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout()); // edited line
setVisible(true);
setSize(800,420);
setLocationRelativeTo(null);
setIgnoreRepaint(true);
createBufferStrategy(2);
bs = getBufferStrategy();
panel.setIgnoreRepaint(true);
panel.setPreferredSize(new Dimension(800,420));
add(panel, BorderLayout.CENTER); // edited line
panel.drawStuff();
}
public class DrawPanel extends JPanel {
public void drawStuff() {
while(true) {
try {
Graphics2D g = (Graphics2D)bs.getDrawGraphics();
g.setColor(Color.BLACK);
System.out.println("W:"+getSize().width+", H:"+getSize().height);
g.fillRect(0,0,getSize().width,getSize().height);
bs.show();
g.dispose();
Thread.sleep(20);
} catch (Exception e) { System.exit(0); }
}
}
}
public static void main(String[] args) {
BSTest bst = new BSTest();
}
}
If you are having only one panel in frame and nothing else then try this:
Set BorderLayout in frame.
Add panel in frame with BorderLayout.CENTER
May be this is happening because of while loop in JPanel.(Not sure why? finding actual reason. Will update when find it.) If you replace it with paintComponent(g) method all works fine:
public BSTest() {
//--- your code as it is
add(panel, BorderLayout.CENTER);
//-- removed panel.drawStuff();
}
public class DrawPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
System.out.println("W:" + getSize().width + ", H:" + getSize().height);
g2d.fillRect(0, 0, getSize().width, getSize().height);
}
}
//your code as it is.
Here's an alternative using pack instead.
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PackExample extends JFrame {
public PackExample(){
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(800,600));
panel.setBackground(Color.green);
add(panel);
pack();
setVisible(true);
}
public static void main(String[] args){
new PackExample();
}
}
This took me forever to figure out but its actually the simplest code ever.
Just create a parent panel and pass GridLayout then add your child panel like this.
JPanel parentPanel= new JPanel(new GridLyout());
JPanel childPanel= new JPanel();
parentPanel.add(childPanel);
If you want to fill the JFrame with the whole of JPanel you need to setUndecorated to true i.e. frame.setUndecorated(true);. But now you have to worry about your MAXIMIZE< MINIMIZE, and CLOSE Buttons, towards the top right side(Windows Operating System)