Good day,
Hopefully this is a quick kill question. I am writing an application that uses JPanels and JLayeredPane inside a JFrame. On the initial start of my application, one of the panels does not show up until my mouse moves over the area where the panel should be. I even call the validate and repaint methods, but I am still able to display both panels together. Any suggestions? Thank you.
Here is my JFrame class (which has the main method)
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class Application extends JFrame
{
public Application()
{
this.setSize(500,500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
JLayeredPane lp = new JLayeredPane();
lp.setBounds(0,0,500,500);
this.setLayeredPane(lp);
Mypanel p1 = new Mypanel();
Mypanel2 p2 = new Mypanel2();
this.getLayeredPane().add(p1,0);
this.getLayeredPane().add(p2,1);
this.validate();
this.repaint();
this.validate();
}
public static void main(String[] args)
{
Application app = new Application();
}
}
Here is one of my panel classes
import javax.swing.JButton;
import javax.swing.JPanel;
public class Mypanel extends JPanel
{
public JButton button;
public Mypanel()
{
this.setLayout(null);
this.setBounds(0, 0, 500, 500);
JButton b = new JButton("Hello");
b.setBounds(20,20,300,300);
this.add(b);
}
}
And finally my last panel class
import javax.swing.JButton;
import javax.swing.JPanel;
public class Mypanel2 extends JPanel
{
public JButton button;
public Mypanel2()
{
this.setLayout(null);
this.setBounds(0, 0, 500, 500);
JButton b = new JButton("SUP");
b.setBounds(20,10,200,200);
this.add(b);
this.repaint();
this.validate();
this.repaint();
}
}
First off, in a valid program only JComponent repaints itself. If at some point you find that calling c.repaint() from your controller code fixes some problem, you have neglected basic contracts that at the core of swing framework. And this is never a good idea. So removing all those repaint and validate calls is a good start. Next important thing is understanding how lightweight swing components go about painting their children. There are 2 modes: optimized and not optimized. The first one is only applicable when siblings don't overlap eachother in the container. If they do and optimized painting is on, you are going to get all sorts of weird behavior when those components repaint themselves(like when you hover mouse pointer over them). All lightweight components can handle overlapping children through setComponentZOrder() . JLayeredPane only introduces the concept of a layer as a means of controlling zorder in a more flexible way. It tries to be smart about what mode to choose to paint its children but, sadly there are subtleties to how this works. so this code would do what you need:
Mypanel p1 = new Mypanel();
Mypanel2 p2 = new Mypanel2();
getLayeredPane().setLayer(p1,0);
getLayeredPane().setLayer(p2,1);
getLayeredPane().add(p1);
getLayeredPane().add(p2);
and this wouldn't:
Mypanel p1 = new Mypanel();
Mypanel2 p2 = new Mypanel2();
getLayeredPane().add(p1);
getLayeredPane().add(p2);
getLayeredPane().setLayer(p1,0);
getLayeredPane().setLayer(p2,1);
the trick is to call setLayer before you add children to the container so that JLayeredPane would turn off optimized painting.
BTW I couldn't help but wonder why JLayeredPane? If you need switching programmatically between different layouts maybe JTabbedPane is your answer anyways
JLayeredPane lp = new JLayeredPane();
JPanel d = new JPanel();
d.setVisible(true);
d.setBounds(10, 10, 556, 386);
lp.add(d, new Integer(0), 0);
Related
I need to create a 8x8 grid where each tile is a JButton using two nested for loops. I’ve tried and tried but can’t get it done.
Needed for a game of Reversi (Othello) assignment.
Oracle has a nifty tutorial, Creating a GUI With JFC/Swing, that will take you through the steps of creating a Swing GUI. Skip the Netbeans section.
Here's a GUI with an 8 x 8 grid of JButtons.
The first thing I did was make a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a JFrame. The JFrame methods must be called in a specific order. This is the order I use for most of my Swing applications.
I created a JPanel. The JPanel uses a GridLayout to lay out the 64 JButtons I created.
Swing components should always be constructed in a JPanel. A JFrame has a default BorderLayout that allows you to place up to nine JPanels on the JFrame. Even though it's possible, you should never place any Swing components other than JPanels or JScrollPanes directly on a JFrame.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class OthelloGUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new OthelloGUI());
}
#Override
public void run() {
JFrame frame = new JFrame("Othello GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new GridLayout(0, 8));
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
for (int index = 0; index < 64; index++) {
JButton button = new JButton();
button.setPreferredSize(new Dimension(64, 64));
panel.add(button);
}
return panel;
}
}
I'm trying to learn Java and JFrames. I put together this simple app where the Main class launches the MainGui class. MainGui will contain a bunch of buttons that each does something different (right now it only has the canadaFlagButton). I have no problem getting the window for MainGui to open with the button I created.
However, when I click the canadaFlagButton, it is supposed to launch a new window but instead, nothing happens.
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class MainGui extends JFrame implements ActionListener {
JButton canadaFlagButton;
MainGui() {
canadaFlagButton = new JButton("Canada Flag");
canadaFlagButton.setBounds(100, 100, 200, 50);
canadaFlagButton.setFocusable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(400, 600);
this.setLayout(null);
this.add(canadaFlagButton);
this.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==canadaFlagButton) {
CanadaFlag window1 = new CanadaFlag();
}
}
}
//Can I draw the Canadian flag?
This is the CanadaFlag class:
import javax.swing.*;
import java.awt.*;
public class CanadaFlag extends JFrame {
CanadaFlag() {
//creating label for maple leaf
JLabel leafLabel = new JLabel();
ImageIcon leaf = new ImageIcon("mapleleaf.png");
leafLabel.setIcon(leaf);
leafLabel.setVerticalAlignment(JLabel.CENTER);
leafLabel.setHorizontalAlignment(JLabel.CENTER);
JPanel redLeftPanel = new JPanel();
redLeftPanel.setBackground(Color.red);
redLeftPanel.setBounds(0, 0, 400, 1000);
JPanel redRightPanel = new JPanel();
redRightPanel.setBackground(Color.red);
redRightPanel.setBounds(1000, 0, 400, 1000);
JPanel whiteMiddlePanel = new JPanel();
whiteMiddlePanel.setBackground(Color.white);
whiteMiddlePanel.setBounds(400, 0, 600, 1000);
whiteMiddlePanel.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setSize(1400, 1000);
this.setVisible(true);
this.add(redLeftPanel);
this.add(redRightPanel);
this.add(whiteMiddlePanel);
whiteMiddlePanel.add(leafLabel);
}
}
An application should only have a single main JFrame. If you need other windows then you would typically use a JDialog.
Swing was designed to be used with layout manager. If you are trying to learn Swing then check out the Swing tutorial on Layout Managers for more information and working example.
it is supposed to launch a new window but instead, nothing happens
You never add the ActionListener to the button.
canadaFlagButton.addActionListener( this );
The tutorial also has a section on How to Use Buttons that contains a working example. I suggest you keep a reference to the tutorial for examples of all Swing basics.
I'm having issues with my code regarding the fact that when I instantiate my City class as an object and add it to the right side of my JSplitPane (or even the left), the circle that is supposed to be drawn is not showing up. My cities class uses paintComponent and should draw a circle just by calling the constructor. I have also tried putting the repaint in its own drawIt() method but the result is still the same. The buttons and spinner show up on the left side of the divider, but the circle I am trying to draw does not show up at all.
Here is my City class.
import javax.swing.*;
import java.awt.*;
public class City extends JPanel{
int xPos, yPos;
City(int x, int y){
xPos = x;
yPos = y;
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(xPos, yPos, 10, 10);
}
}
And here is my main.
Here I try to instantiate my city and add it to the right side of the JSplitPane (under Add Components) and that is where I am having issues with, as the black circle will not be drawn on the JSplitPane.
import java.awt.*;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
public class TSP{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TSP();
}
});
}
TSP(){
JLabel instructions = new JLabel("Enter the number of cities: ");
instructions.setBounds(30, 150, 300, 40);
SpinnerNumberModel numMod = new SpinnerNumberModel(2, 2, 10, 1);
JSpinner numOfCities = new JSpinner(numMod);
numOfCities.setBounds(185, 150, 80, 40);
JButton start = new JButton("Start Simulation");
start.setBounds(50, 400, 200, 40);
JFrame frame = new JFrame("Travelling Salesperson");
JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
JPanel lp = new JPanel(null);
JPanel rp = new JPanel(null);
sp.setDividerLocation(300);
sp.setLeftComponent(lp);
sp.setRightComponent(rp);
sp.setEnabled(false);
frame.setDefaultCloseOperation(3);
frame.setSize(1100,600);
frame.setResizable(false);
////////////////Add Components//////////////////////////
lp.add(instructions);
lp.add(numOfCities);
lp.add(start);
City test = new City(301, 301);
rp.add(test);
frame.add(sp);
////////////////////////////////////////////////////////
frame.setVisible(true);
}
}
I feel like the circle is being drawn under the JSplitPane as if I add my cities object (test) to my frame instead of the JSplitPane(frame.add(test) instead of rp.add(test) under the Add Components section) the black circle will appear in the desired spot but the JSplitPane along with the buttons and spinners will disappear so I feel as if they are conflicting. Is there any fix to this or is there another way altogether to make the circle appear on the right side while the other components are on the left side.
I do not know why it is not drawing the circle on the JSplitPane, but any sort of help would be appreciated. Thanks!
Sorry if anything is unclear or there is any ambiguity in my code, or if I need to post more information as I am quite new to posting here. Let me know if there is anything else I need to add or if there are any questions regarding what I am asking!
EDIT:
It seems there is something blocking where I draw the circle, like another JPanel. Here is an image below. As you can see part of the circle looks as if it is being covered. The small box I drew is the only area that the dot is visible from (everywhere else the circle is covered up by white). Also, the coordinates for the circle in the image below is at (3, 0), i.e City test = new City(3, 0);
I am not quite sure why this is happening though.
the invisible JPanel?
Now that I've seen what you're trying to do, I can provide a more proper answer.
You have a control panel on the left and a drawing panel on the right. Usually, you don't use a JSplitPane to separate the panels. To create your layout, you would add the control panel to the LINE_START of the JFrame BorderLayout and the drawing panel to the CENTER.
The reason for this is that you don't want to constantly recalculate the size of the drawing panel.
So let me show you one way to get a solid start. Here's the GUI I created.
Here are the things I did.
All Swing GUI applications must start with a call to the SwingUtilities invokeLater method. This method ensures that Swing components are created and executed on the Event Dispatch Thread.
I separated the creation of the JFrame, the control panel, and the drawing panel. That way, I could focus on one part of the GUI at a time.
The JFrame methods must be called in a certain order. This is the order that I use for most of my Swing applications.
The JFrame is not sized. It is packed. The Swing layout managers will calculate the size of the components and the JPanels.
I used a FlowLayout and a GridBagLayout to create the control panel. Yes, this looks more complicated than absolute positioning, but in the long run, layout managers allow the GUI to be more flexible.
I used the setPreferredSize method in the drawing panel to set the preferred size of the drawing panel. Because I know the drawing panel size, I can put the first city in the center of the drawing panel.
And here's the code. You don't have to code exactly like this, but this code should give you a good basis to start your project. Take a look at the model / view / controller pattern and see how to further separate your code into smaller pieces that allow you to focus on one part of your application at a time.
I put all the classes in one file to make it easier to paste. You should separate these classes into separate files.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
public class CitySimulation implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new CitySimulation());
}
private ControlPanel controlPanel;
private DrawingPanel drawingPanel;
private JFrame frame;
#Override
public void run() {
frame = new JFrame("Traveling Salesperson");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
controlPanel = new ControlPanel();
frame.add(controlPanel.getPanel(), BorderLayout.LINE_START);
drawingPanel = new DrawingPanel();
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class ControlPanel {
private JPanel panel;
public ControlPanel() {
panel = new JPanel(new FlowLayout());
JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.LINE_START;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(10, 10, 10, 10);
JLabel instructions = new JLabel("Enter the number " +
"of cities:");
mainPanel.add(instructions, gbc);
gbc.gridx++;
gbc.insets = new Insets(10, 0, 10, 10);
SpinnerNumberModel numMod =
new SpinnerNumberModel(2, 2, 10, 1);
JSpinner numOfCities = new JSpinner(numMod);
mainPanel.add(numOfCities, gbc);
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
gbc.insets = new Insets(10, 10, 10, 10);
JButton start = new JButton("Start Simulation");
mainPanel.add(start, gbc);
panel.add(mainPanel);
}
public JPanel getPanel() {
return panel;
}
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(400, 400));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillOval(195, 195, 10, 10);
}
}
}
First of all, this is a more specific question than it seems to be. To start off: I am currently doing a small application with a rather small GUI, so I decided to make a GUI class, and initialize my whole GUI in this constructor.
This would look like this:
public class GUI extends JFrame{
public GUI{
//Initialize GUI here, including its Frames, Panels, Buttons etc.
}
}
How can I now access the GUIs frame etc. from an external class? If I would create an object of the GUI class, I would simply duplicate my GUI window. I did not come across any other ideas than making the frame, panel and so on static.
I'm somewhat lost right now. Also I'm pretty sure that I am not thinking the right way into this case, but I need someone to point me to the right direction. If someone could help me out, I would be very thankful.
First of all, using static is the worst solution possible, even if your GUI class is a singleton (buf if it is, at least it will work fine).
Why don't you simply create getters and/or setters ? And finally, it is usually not normal that external classes need to access the components of another graphic class. You should wonder if your design is the most fitted for your needs.
Here's a simple GUI to change the background color of a JPanel with a JButton. Generally, this is how you construct a Swing GUI.
package com.ggl.testing;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ChangeDemo implements Runnable {
private boolean isYellow;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new ChangeDemo());
}
#Override
public void run() {
frame = new JFrame("Change Background Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
JPanel namePanel = new JPanel();
JLabel nameLabel = new JLabel(
"Click the button to change the background color");
nameLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
namePanel.add(nameLabel);
mainPanel.add(namePanel);
final JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
buttonPanel.setBackground(Color.YELLOW);
isYellow = true;
JButton changeButton = new JButton("Change Color");
changeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
isYellow = !isYellow;
if (isYellow) buttonPanel.setBackground(Color.YELLOW);
else buttonPanel.setBackground(Color.RED);
}
});
buttonPanel.add(changeButton);
mainPanel.add(buttonPanel);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
You don't access the Swing components of the GUI from other classes. You create other classes to hold the values of the GUI.
Generally, you use the model / view / controller pattern to construct a Swing GUI. That way, you can focus on one part of the GUI at a time.
Take a look at my article, Java Swing File Browser, to see how the MVC pattern works with a typical Swing GUI.
You don't need to make it static or to create a new JFrame object every time.
Have a look at this simple code :
class UseJFrame {
public static void main(String...args) {
Scanner sc = new Scanner(System.in);
JFrame frame = new GUI();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
System.out.println("Press E to exit");
String ip;
while(true) {
System.out.println("Show GUI (Y/N/E)? : ");
ip = sc.nextLine();
if(ip.equalsIgnoreCase("y") {
frame.setVisible(true);
} else if(ip.equalsIgnoreCase("n") {
frame.setVisible(false);
} else { // E or any other input
frame.dispose();
}
}
}
}
Note : Don't make GUI visible through constructor or it will show window at the very starting of creation of JFrame object.
If you want to use the same JFrame object at other places too then pool architecture would be better approach.
it's my first post so I hope it'll not be too cringeworthy. So I am trying to create a hex-based strategy game, not quite there yet but anyways.
To achieve a hex-based game I would like to create a field made of hexes which the user should be able to click, and receive the coordinates of that pixel. At the moment I can produce either a field of hexes or a mouselistener/mouseadapter but not both. The last one executed replaces the other on the screen.
If the pane.add(New HexMap()); is switched with pane.add(new MouseListener()); the listener works but the line is not printed
I've looked around for quite some time but the posts that I've encountered had either dealt with changing the background color which the mouselistener can do, because background is independent of the mousesensorhttp://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html? The other examples I've come by have been too advanced for me, because they're using multiple panes, and I have not been able to comprehend themhttp://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html.
So what I'm looking for is a way to add a mouselistener over a single pane, displaying the hexes. Would this be possible?
E.G adding the hexMap after the mouselistener would not overwrite the mouselistener but rather act as an addition
A single line has been created acting as a placeholder for the hexes.
The code:
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class GraphicsSetup extends JPanel{
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT= 400;
private static JFrame frame;
public static void main(String[] args){
GraphicsSetup draw = new GraphicsSetup();
}
public GraphicsSetup(){
HexMap hexMap = new HexMap();
JPanel panel = new JPanel();
frame = new JFrame("HexExample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
Container pane = frame.getContentPane();
pane.setBackground(new Color(20, 100, 30));
pane.add(new MouseListener());
pane.add(new HexMap());
frame.setVisible(true);
}
public class HexMap extends JComponent{
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.drawLine(0,0, FRAME_WIDTH, FRAME_HEIGHT);
}
}
class MouseListener extends JComponent{
public MouseListener(){
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
System.out.println("Mouse Event" + me);
}
});
}
}
}
Yours Sincerely
I'm not entirely sure what you're after, but try adding your components to your panel object. Such as:
panel.add(new MouseListener());
panel.add(new HexMap());
And then add this to the content pane of your frame:
pane.add(panel);
If you're wondering how to arrange your interface differently, read about layout managers here:
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
Edit
Try the following:
Set the layout manager to use a BorderLayout:
JPanel panel = new JPanel(new BorderLayout());
Add your components to the panel and set their location:
panel.add(new MouseListener(), BorderLayout.NORTH);
panel.add(new HexMap(), BorderLayout.CENTER);
Add the panel to the frame content pane:
pane.add(panel);
This will work but the size of the MouseListener panel is quite small...you'll need to figure that out next...