I'm trying to create a binary clock in Java using swing.
I encountered a problem on very beginning, where last element of label array is always appearing on the 0 x axis position and in the middle of y axis.
I don't want to use any layout menager since i want few circle images on my Panel.
Here's my code so far:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame extends JFrame {
public MainFrame()
{
setLayout(null);
JFrame frame = new JFrame("Binary Clock");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(600,400);
frame.setResizable(false);
JLabel label[] = new JLabel[5];
for(int i = 0; i < 5; i++)
{
label[i] = new JLabel();
label[i].setLocation(i * 10, i * 10);
label[i].setSize(30, 10);
label[i].setText("TEST");
frame.add(label[i]);
}
}
public static void main(String[] args)
{
new MainFrame();
}
}
i found similar question here:
setLocation of Label
but there is no answer how to do it without layout manager.
I Tried resizing and stuff, but only setText() can do any difference, nothing besides that works.
Thank you!
Pshemo's answer provides the answer as to why you're getting the odd behavior with the last label added, but I've marked up the code with a couple enhancements as well.
Remove the extends JFrame, this is rarely a good idea and you already saw why using an instance of a JFrame within a class that is a JFrame can cause...
Set the layout of the frame instance to null (rather than that of this) that you have originally. Note: Use of a layout manager is the preferred method to creating Swing UI's, and is generally discouraged to set this to null
You don't need the array of labels (unless you plan on using them later), you can just use the loop to add what you want.
Make the frame visible last
Then you have:
import javax.swing.*;
#SuppressWarnings("serial")
public class MainFrame {
public MainFrame()
{
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600,400);
frame.setResizable(false);
for(int i = 0; i < 5; i++)
{
JLabel label = new JLabel();
label.setLocation(i * 10, i * 10);
label.setSize(30, 10);
label.setText("TEST");
frame.add(label);
}
frame.setVisible(true);
}
public static void main(String[] args)
{
new MainFrame();
}
}
I am not Swing specialist so I don't know what consequences will give you null layout manager but from what I remember it is nothing good so consider using proper layout manager instead of null.
Anyway it seems that your problem is caused by fact that you set your null layout in currently initialized by constructor this instance, rather than instance from form regerence.
So instead of
setLayout(null);// represents this.setLayout(..), not frame.setLayout(..)
JFrame frame = new JFrame("Binary Clock");
use
JFrame frame = new JFrame("Binary Clock");
frame.setLayout(null);
I am also not sure why your class extends JFrame. Your code doesn't actually look like it needs any of inherited features from JFrame.
I don't want to use any layout menager since i want few circle images on my Panel.
Swing was designed at the core to use layout managers, it how it's update process works and how it deals with the difference in rendering pipelines across multiple platforms. Sure, you can try and do without it, but in the end, you'd simply reinvent the wheel as you try and compensate for all the little niggly issues that are created while trying to do without it.
The following simply uses a GridBagLayout, which provides me with the ability to supply gridx/y positions to the components (and other nice things)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class BinaryClock {
public static void main(String[] args) {
new BinaryClock();
}
public BinaryClock() {
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 {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// Hour column
gbc.gridx = 0;
addColumn(2, gbc);
gbc.gridx = 1;
addColumn(4, gbc);
// Minute column
gbc.gridx = 3;
addColumn(3, gbc);
gbc.gridx = 4;
addColumn(4, gbc);
// Minute column
gbc.gridx = 6;
addColumn(3, gbc);
gbc.gridx = 7;
addColumn(4, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
gbc.gridheight = GridBagConstraints.REMAINDER;
add(new JSeparator(JSeparator.VERTICAL), gbc);
gbc.gridx = 5;
add(new JSeparator(JSeparator.VERTICAL), gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void addColumn(int count, GridBagConstraints gbc) {
for (int index = 0; index < count; index++) {
gbc.gridy = 3 - index;
add(new Dot(), gbc);
}
}
}
public class Dot extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.setColor(Color.DARK_GRAY);
g2d.fillOval(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
}
This now frees you up to worry about how to make the clock update without need to continuously try and fix issues in the layout
Related
I want to have 2 JPanels in my app, side by side. One that will have some info about my custom board on the right and one about painting that custom board on the left. The first JPanel is a classic, but the second is a custom panel. It seems that im having problems with putting my custom panel into the frame.
I've created a class named BoardPanel within my gui class to draw my custom board. I don't know if this is the best approach. Should i create a separate class instead?
This is the code of the gui class:
public class BattleshipGUI extends JFrame {
private BoardPanel mainPanel;
///////////////////////////////////////////////////////////////////////////////////////////////
// Create my frame
///////////////////////////////////////////////////////////////////////////////////////////////
public BattleshipGUI() {
JPanel container = new JPanel(new BorderLayout()); //the container panel that contains the 2 other panels
mainPanel = new BoardPanel(); //main panel with my custom painting
JPanel detailsPanel = new JPanel(new BorderLayout()); //secondary panel with various details about the game
container.add(mainPanel, BorderLayout.CENTER); //add the 2 panels in the container
container.add(detailsPanel, BorderLayout.EAST);
this.add(container); //add container to my frame
//this.setContentPane(container);
this.setIconImage(Toolkit.getDefaultToolkit().getImage(BattleshipGUI.class.getResource("/resources/battleship_128.png")));
this.setTitle("My Battleship Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//this.setBounds(100, 100, 850, 700);
//this.pack();
this.setSize(850, 600);
this.setVisible(true);
}
And this is the code of the inner class for the custom painting
class BoardPanel extends JPanel {
private static final int ROWS = 20;
private static final int COLUMNS = 20;
public void paintComponent(Graphics g) {
super.paintComponent(g);
int sqSize = this.getHeight()/ROWS;
for(int i=0; i<ROWS; i++) {
for(int j=0; j<COLUMNS; j++) {
int x = j * sqSize;
int y = i * sqSize;
g.drawRect(x, y, sqSize, sqSize);
}
}
}
}
Aside from all these, i have a question. If i want to have a custom painting, is it possible to work along side with the WindowsBuilderPro? I begun using that tool at first. But, i saw that i cant draw something custom with the tool and i had to write code to do that. Is it possible to write code for a custom paint AND use the tool at the same time for different purposes, like adding a simple text label, or even to edit that custon paint? The expected result that i want to see, appears when i run the program. My frame with the two panels. But when i open the WindowsBuilderPro, my custom panel does not appear and the result is a bit wrong. Thit is the reason why i have a question about my approach and if i can write code and use the tool at the same time. Thank you and sorry for the long text guys. I am too confused about this.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
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 {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = gbc.BOTH;
gbc.gridx = 0;
gbc.gridy = 0;
JPanel filler = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 300);
}
};
filler.setBackground(Color.BLUE);
add(filler, gbc);
gbc.gridx++;
add(new BoardPanel(), gbc);
}
}
class BoardPanel extends JPanel {
private static final int ROWS = 20;
private static final int COLUMNS = 20;
private int sqSize = 20;
#Override
public Dimension getPreferredSize() {
return new Dimension(COLUMNS * sqSize, ROWS * sqSize);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLUMNS; j++) {
int x = j * sqSize;
int y = i * sqSize;
g.drawRect(x, y, sqSize, sqSize);
}
}
}
}
}
Take the time to read through Laying Out Components Within a Container to get a better understanding how the layout management API works
I'm developping a sprite editor. I have a class that extends JPanel, in that class, I use a JLayeredPane as a container. On the bottom layer, there is a JLabel with an ImageIcon and on the top Layer there is a JPanel where I have drawn a grid. When the code is ran, I make some basic math to set the JLayeredPane's prefered size to be equal to about 85% of the height of the screen resolution.
My problem is that when the user wants a new canvas to draw on it, I ask with a JOptionPane the user the size of the canvas he wants. Then I call my class constructor to create a new canvas with the specified size. After that I apply the methods revalidate() and repaint() on the canvas. Unfortunately, it doesn't work. In addition, when I try to get the width and the height of my canvas, it gives 0 for both. However, when I set the size by myself directly into the code, it works well. Therefore, I would like to know how can I update the size of a JLayeredPane ?
On start with a size of 85% of the screen resolution
When I set the size to 640x640 directly inside my code
When I answer the JOptionPane with a size of 640x640, the grid has been adapted to the asking size. However, it's not the case for the JLayeredPane
Without more context, it's impossible to know what's going wrong with your code.
However, personally, I'd stop trying to reinvent the wheel and make use of the available layout management API to provide you with the support you need.
There's a number of ways you could approach the problem. Personally I'd wrap the rendering of the grid and the image up into a single component, but that's me. In that case, you wouldn't need the JLayeredPane.
Let's, for the moment, assume that the JLayeredPane is non-negotiable. I'd then apply a layout manager to the JLayeredPane, this makes interaction with the two other components (image and grid) simpler, as the layout manager takes over control. Because you want them overlaid onto of each other, I'd be tempted to use a GridBagLayout.
The problem here then becomes keeping the two component's sizes in sync with each other. To this end, I'd use setPreferredSize to change the size dynamically.
"insert internal screaming" - Any time I see setPreferredSize it sets of alarm bells. In this case, again, I'd fall back to using a single component and provide a "sizing" mechanism which worked with getPreferredSize, but at the end of day, this is moving us in the same direction.
For laying out the JLayeredPane at 85% height of the parent container, again, GridBagLayout would be my preferred choice.
The following example lays the JLayeredPane in such away as it will want to fill the available space of the parent container (up to 85% of the available height). It then allows the image and grid components to automatically position themselves within side this area.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private JLayeredPane lp;
private ImagePane imagePane;
private GridPane gridPane;
public TestPane() throws IOException {
setLayout(new GridBagLayout());
lp = new JLayeredPane();
lp.setLayout(new GridBagLayout());
imagePane = new ImagePane();
gridPane = new GridPane();
gridPane.setForeground(new Color(255, 255, 255, 64));
imagePane.setImg(ImageIO.read(...)));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
lp.add(imagePane, gbc);
lp.add(gridPane, gbc);
lp.setLayer(imagePane, 0);
lp.setLayer(gridPane, 10);
lp.setBackground(Color.RED);
lp.setBorder(new LineBorder(Color.RED));
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 0.85;
gbc.fill = gbc.BOTH;
add(lp, gbc);
applyDesiredSize(200, 200);
JButton btn = new JButton("Change");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weighty = 0.25;
add(btn, gbc);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JTextField widthTF = new JTextField(4);
JTextField heightTF = new JTextField(4);
JPanel panel = new JPanel();
panel.add(new JLabel("Size: "));
panel.add(widthTF);
panel.add(new JLabel("x"));
panel.add(heightTF);
JOptionPane.showMessageDialog(TestPane.this, panel, "Change Size", JOptionPane.PLAIN_MESSAGE);
try {
int width = Integer.parseInt(widthTF.getText());
int height = Integer.parseInt(heightTF.getText());
applyDesiredSize(width, height);
} catch (NumberFormatException numberFormatException) {
JOptionPane.showMessageDialog(TestPane.this, "Invalid dimensions", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
protected void applyDesiredSize(int width, int height) {
Dimension size = new Dimension(width, height);
// lp.setPreferredSize(size);
imagePane.setPreferredSize(size);
gridPane.setPreferredSize(size);
// Stop GridBagLayout from shrinking the components
imagePane.setMinimumSize(size);
gridPane.setMinimumSize(size);
lp.revalidate();
lp.repaint();
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public BufferedImage getImg() {
return img;
}
public void setImg(BufferedImage img) {
this.img = img;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
}
public class GridPane extends JPanel {
private int gridSize = 10;
public GridPane() {
setOpaque(false);
}
public int getGridSize() {
return gridSize;
}
public void setGridSize(int gridSize) {
this.gridSize = gridSize;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getForeground());
for (int x = 0; x < getWidth(); x += gridSize) {
g2d.drawLine(x, 0, x, getHeight());
}
for (int y = 0; y < getWidth(); y += gridSize) {
g2d.drawLine(0, y, getWidth(), y);
}
g2d.dispose();
}
}
}
So, before you shoot me down because it "doesn't fit with what you've done", understand that:
I've no context to go on as to what you've actually done, other then an overview of your problem description
I've provided a runnable example which demonstrates the basic concepts you "might" be able to use to solve your issue, which is more then you've done for us
This is not the only way you might achieve this, and by no means is it my preferred solution
This question already has answers here:
Why does the first panel added to a frame disappear?
(2 answers)
Closed 6 years ago.
public class Board extends JFrame
{
public void bd()
{
JFrame frame=new JFrame();
JLabel background1 = new JLabel(new ImageIcon("background.png"));
JLabel knight=new JLabel(new ImageIcon("knight.jpg"));
frame.add(background1);
frame.add(knight);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
I've been having some trouble with my code
when i add the knight image the background image will disappear and only the knight image appears.
How would i make the images overlap or make the background image act like a background
JFrame uses BorderLayout by default, which means only one image is managed at the default/CENTER position.
See How to Use Borders for more details.
The solution is actually difficult, because you need to be able to make guarantees about the size of the layout, the images and the board
You might be able to use a GridLayout or GridBagLayout, but you'd need to have "empty" filler components to allow the layout to correctly expand, which could be come troublesome
Maybe a better solution might be to use a custom painting approach, which give you control over where the images are placed.
Painting in AWT and Swing and Performing Custom Painting for more detals
The following example simply makes use of GridBagLayout, which allows you to overlay components (in a variety of different ways). This example makes the boardLabel the container for all the other pieces, but, because the way GridBagLayout works, this isn't an absolute requirement
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Chess {
public static void main(String[] args) {
new Chess();
}
public Chess() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
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);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
public TestPane() throws IOException {
BufferedImage board = ImageIO.read(getClass().getResource("/board.png"));
BufferedImage knight = ImageIO.read(getClass().getResource("/Knight.png"));
setLayout(new BorderLayout());
JLabel boardLabel = new JLabel(new ImageIcon(board));
add(boardLabel);
boardLabel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
for (int row = 0; row < 8; row++) {
gbc.gridy = row;
for (int col = 0; col < 8; col++) {
gbc.gridx = col;
boardLabel.add(filler(), gbc);
}
}
JLabel knightLabel = new JLabel(new ImageIcon(knight));
gbc.gridx = 3;
gbc.gridy = 4;
boardLabel.add(knightLabel, gbc);
}
protected JComponent filler() {
JLabel filler = new JLabel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(50, 50);
}
};
return filler;
}
}
}
With a setup like this, you can simply use GridBagConstraint's gridx and gridy values as direct cell address (no need to calculate the pixel position).
Because of the way the layout manager works, you are required to provide an empty cell component as well
JLabel background1 = new JLabel(new ImageIcon("background.png"));
JLabel knight=new JLabel(new ImageIcon("knight.jpg"));
frame.add(background1);
frame.add(knight);
I'm guessing what you really want is have the knight displayed on top (on the z axis) so you need to do something like:
JLabel background1 = new JLabel(new ImageIcon("background.png"));
background.setLayout( new BorderLayout() );
JLabel knight=new JLabel(new ImageIcon("knight.jpg"));
background1.add(knight);
frame.add(background1);
That is you need to follow a parent/child hierarchy:
add the background to the frame
add the knight to the background
This question already has answers here:
How to add JTable in JPanel with null layout?
(11 answers)
Closed 8 years ago.
I know this is horrible coding but I desperately need to fix this problem. I've tried multiple ways of trying to position the button but the button still stays in the top center with all the other buttons lined up after it.
import javax.imageio.ImageIO;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.io.IOException;
import java.net.URL;
public class Template extends JFrame {
/**
* #param args
*/
public static void main(String[] args) throws IOException{
// TODO Auto-generated method stub
JFrame frame = new JFrame("The Impossible Quiz");//Construct JFrame
frame.setLayout(null);//manual setting for button placement
frame.setContentPane(new JPanel() {//sets panel as background
BufferedImage image = ImageIO.read(new URL("https://pbs.twimg.com/media/BoyFVfXIUAA0Tik.png"));//uses url image
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, 1360, 690, this);//sets image as jframe background
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//closes jframe when press exit
frame.setSize(1365, 730);//sets size of jframe
JPanel buttonpanel = new JPanel();//sets panel for all buttons
buttonpanel.setBounds(0, 0, 1460, 690);//sets placing and sizing of panel
buttonpanel.setOpaque(false);//makes panel transparent
JButton next = new JButton ("Start");//contructs correct start button
next.setBounds(10, 5, 40, 50);
buttonpanel.add(next);//adds button to panel
next.addActionListener(new ActionListener()//adds actionlistener to button
{
public void actionPerformed(ActionEvent e)
{
new Introduction();//continues to next question
}
});
JButton wrongstart = new JButton("Start!!");//constructs wrong start button
wrongstart.setSize(100, 400);//setes size of button
buttonpanel.add(wrongstart);//adds button to panel
wrongstart.addActionListener(new ActionListener()//adds actionlistener to button
{
public void actionPerformed(ActionEvent e)
{
new Wrong();//direct user to wrong panel
}
});
frame.add(buttonpanel);//adds panel to jframe
frame.setVisible(true);//sets jframe as visible
}
}
Your problem is that you're trying to use absolute positioning to position a component (your JButton) in a container (the containing JPanel) that uses FlowLayout as default, and FlowLayout completely ignores the components bounds. A quick solution is to set the JPanel's layout to null allowing for absolute positioning. A correct solution is to always avoid null layouts and absolute positioning and instead to nest JPanels, each using its own layouts, in order to create complex yet flexible and pleasing GUI's.
You're setting the JFrame contentPane's layout to null as well -- don't do that either.
And then adding a JPanel as the contentPane which uses a default FlowLayout -- don't do that. Let the contentPane's layout be BorderLayout.
Edit
For example, if we leave the contentPane alone with its BorderLayout, and add another image panel on top of it, one that uses GridBagLayout, we can easily position our JButton to the top left corner of the GUI if desired. ....
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class Template2 extends JPanel {
private static final int PREF_W = 1460;
private static final int PREF_H = 690;
private BufferedImage img;
private JButton startButton = new JButton("Start");
public Template2() {
setLayout(new GridBagLayout());
// TODO: .... read in your image here
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.insets = new Insets(5, 10, 0, 0);
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
add(startButton, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
public void paintComponents(Graphics g) {
super.paintComponents(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
private static void createAndShowGui() {
Template2 mainPanel = new Template2();
JFrame frame = new JFrame("Some Horrendous Program");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have a 100 by 100 grid of labels. I've got a method which creates and populates an array of Strings. The next method creates an array of labels and then adds the String (created from previous method) to the labels using the setText() method. Some of the labels contain images too. Method after that takes those JLabels and adds them to a JPanel of Grid Layout(lets call this x1). Then I've added the JPanel to a JScrollPane(x2), the JScrollPane gets added to another JPanel(x3) with an empty border and this final JPanel(x3) gets added to the JFrame. So that's how I've created the grid and I'm happy with that, I don't want to change it.
I would like to add an image to x1 - the JPanel with Grid Layout.
For this I would have to add the paintComponent method and use the drawImage() method. My question is how will Eclipse know which panel to add the image to? I's prefer not to create a separate class for x1, I did that before and it just didn't work out right and I rather not go down that incredibly frustrating road again, I'm sorry!
I have considered using a Glass Pane however I would no longer be able to see the images of the JLabels - which is really important.
I think adding the image to the background of the JPanel will be best because I also want to have a button which shows/hides the grid lines - the borders of the JLabels.
I hope I'm making sense.
Below is the code. I understand it's a lot of code in one class. I did have it in two separate classes but it just didn't work me. I find this much easier. I hope you don't mind
package roverMars;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
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.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
public class MenuPanel extends JPanel {
private static final long serialVersionUID = -3928152660110599311L;
public JPanel frame, textfield, buttons, cpPanel;
public JTextField Commands;
public JButton Plot, Submit, Undo;
public JLabel Position, cpLabel;
public Border loweredetched;
public JCheckBox gridLines;
public SubmitButton sub;
static final int rows = 100, columns = 100;
// ******IMAGES******
static BufferedImage North, South, West, East;
public void ImageLoader() {
try {
North = ImageIO.read(this.getClass().getResource("North.png"));
South = ImageIO.read(this.getClass().getResource("South.png"));
West = ImageIO.read(this.getClass().getResource("West.png"));
East = ImageIO.read(this.getClass().getResource("East.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Error occured: " + e);
e.printStackTrace();
}
}
// ******IMAGES******
public void createMenu(JPanel p) {
// Text Field Panel
Commands = new JTextField(20);
textfield = new JPanel();
textfield.setPreferredSize(new Dimension(150, 50));
textfield.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
textfield.setBackground(new Color(204, 153, 255));
textfield.add(Commands);
// Have a button next to the Text Field to clear contents.
// Might need to give the JPanel a new Flow Layout.
// Buttons Panel
buttons = new JPanel();
buttons.setPreferredSize(new Dimension(150, 250));
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
buttons.setBackground(new Color(170, 051, 170));
// Create and Add buttons to the Buttons Panel
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Plot = new JButton("Plot");
Plot.setAlignmentX(Component.CENTER_ALIGNMENT);
Plot.setAlignmentY(Component.CENTER_ALIGNMENT);
buttons.add(Plot);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Submit = new JButton("Submit");
Submit.setAlignmentX(Component.CENTER_ALIGNMENT);
Submit.setAlignmentY(Component.CENTER_ALIGNMENT);
Submit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
sub = new SubmitButton();
sub.Submit(Commands);
cpLabel.setText("*****SET CURRENT POSITION*****");
labels[2][2].setIcon(new ImageIcon(North));
// I will be able to move the rover from here using for loops
// and if statements.
}
});
buttons.add(Submit);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
Undo = new JButton("Undo");
Undo.setAlignmentX(Component.CENTER_ALIGNMENT);
Undo.setAlignmentY(Component.CENTER_ALIGNMENT);
buttons.add(Undo);
buttons.add(Box.createRigidArea(new Dimension(30, 10)));
gridLines = new JCheckBox();
gridLines.setText("Show gridlines");
gridLines.setAlignmentX(Component.CENTER_ALIGNMENT);
gridLines.setAlignmentY(Component.CENTER_ALIGNMENT);
gridLines.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// Set the colour of the JLabels array from here.
System.out.println("clicked");
}
});
buttons.add(gridLines);
buttons.add(Box.createRigidArea(new Dimension(30, 20)));
loweredetched = BorderFactory
.createEtchedBorder(EtchedBorder.RAISED);
cpLabel = new JLabel("Current position: ", JLabel.CENTER);
cpPanel = new JPanel();
cpPanel.setBackground(new Color(153, 153, 204));
cpPanel.add(cpLabel);
cpPanel.setBorder(loweredetched);
// Panel for the main window
JPanel frame = new JPanel();
frame.setPreferredSize(new Dimension(150, 350));
frame.setLayout(new BorderLayout());
frame.add(textfield, BorderLayout.NORTH);
frame.add(buttons, BorderLayout.CENTER);
// This Main Panel
p.setPreferredSize(new Dimension(350, 700));
p.setBackground(new Color(153, 153, 204));
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
p.setBorder(BorderFactory.createEmptyBorder(10, 50, 10, 25));
p.add(Box.createRigidArea(new Dimension(100, 100)));
p.add(frame);
p.add(Box.createRigidArea(new Dimension(15, 15)));
p.add(cpPanel);
p.add(Box.createRigidArea(new Dimension(100, 300)));
}
// From line 142 to 202 is everything to do with creating the Grid
public void StringArray(String[][] labelText) {
int x = 1; // increment rows
for (int i = 0; i < labelText.length; i++) { // x
for (int j = 0; j < labelText.length; j++) { // y
labelText[i][j] = Integer.toString(x); // populate string
x++;
}
}
}
public void JLabelArray(JLabel[][] label, String[][] labelText) {
for (int i = 0; i < label.length; i++) { // x
for (int j = 0; j < label.length; j++) { // y
label[i][j] = new JLabel();
label[i][j].setText(labelText[i][j]);
label[i][j].setOpaque(false);
label[i][j].setBorder(BorderFactory.createLineBorder(new Color(
0, 155, 200), 1));
// label[i][j].setBackground(Color.WHITE);
}
}
}
public void populateGrid(JPanel Grid, JLabel[][] label) { // Add Labels to
// Panel,
String x1[][] = new String[rows][columns];
StringArray(x1);
JLabelArray(label, x1);
Grid.setBackground(Color.RED);
int gHeight = label.length, gWidth = label.length;
Grid.setLayout(new GridLayout(gWidth, gHeight));
for (int i = 0; i < label.length; i++) { // x
for (int j = 0; j < label.length; j++) { // y
Grid.add(label[i][j]);
}
}
}
public void createGrid(JPanel finalPanel, JPanel Grid) {
// Add Grid to Scroll Pane
JScrollPane x4 = new JScrollPane(Grid);
x4.setPreferredSize(new Dimension(600, 600)); // DO NOT DELETE THIS.
x4.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
x4.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
// Add Scroll Pane to another Panel with the Border
finalPanel.setBackground(new Color(153, 153, 204));
finalPanel.setBorder(BorderFactory.createEmptyBorder(50, 25, 50, 50));
finalPanel.add(x4);
}
// Variables for creaeteGUI method.
static MenuPanel t = new MenuPanel();
static JPanel menu = new JPanel();
static JPanel finalPanel = new JPanel();
static JPanel gridPanel = new JPanel();
static JLabel labels[][] = new JLabel[rows][columns];
public static void createGUI() {
t.createMenu(menu);
t.populateGrid(gridPanel, labels);
t.createGrid(finalPanel, gridPanel);
JFrame f = new JFrame();
f.setTitle("Project Testing");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setLocation(100, 100);
f.setAlwaysOnTop(true);
f.setSize(500, 500);
f.add(finalPanel, BorderLayout.CENTER);
f.add(menu, BorderLayout.WEST);
f.pack();
}
public static void main(String args[]) {
createGUI();
t.ImageLoader();
labels[2][2].setIcon(new ImageIcon(West));
}
}
Thank you so much! I really appreciate any help or suggestions :D
As you said what you need to do is to override the paintComponent method of the JPanel and put a drawImage(...) in there. So:
#Override
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
Where image is an instance of the class Image that you loaded previously in the initialization code (don't load it in the paintComponent, that would be too slow and you only want to load it once).
There are 2 ways to accomplish that:
Make your own class extending JPanel and put that code there. You probably will want to create also a method setBackgroundImage(Image) that you can call from you main class to pass the image that you loaded from the disk.
Make an anonymous class, that is doing something similar but without explicitely defining a new class. To do so instead of creating the panel like this:
JPanel gridPanel = new JPanel();
do it like this:
JPanel gridPanel = new JPanel()
{
#Override
public void paintComponent(Graphics g)
{
//super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
Of course you must do this in the actual code (not as an static initialization) since you want to make sure that you load the image before.
Finally a couple of suggestions:
Variable names start in lower case by convention (as opposite to class names that start in upper case). You don't do this for example in the JPanel Grid argument and Comands field.
You are violating Swing's single threading rule. That is, you must call invokeLater in your main wrapping your GUI initializing code. For example look at Swing's Hello World. You can find a detailed explanation of this here.