Wrong packing of JFrame using JPanel in borderlayout - java

I am writing a simple application in Java that does some particle simulation on a bunch of sheep (don't ask). For this I want a window with a JPanel for graphics (which will be resizable with a simple combobox that contains some standard resolutions) and some other elements like buttons to start and pause the simulation etc.
My question: I'm using the JFrame.pack method to pack everything nicely together using a borderLayout. But for some reason the JPanel is packed wrong, it seems like the packing ignores it, so the window is resized to fit the size of only the two buttons that I have now. What am I doing wrong?
This is the code so far (bit of a newbie, so no comments on my dumbness if there is any ;)):
public class Window {
public Sheepness sheepness;
public ButtonPanel buttonPanel;
public PaintPanel paintPanel;
public JFrame frame;
public Window(Sheepness sheepness, int width, int height) {
this.sheepness = sheepness;
frame = new JFrame("Sheepness simulation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//frame.setSize(width, height);
BorderLayout frameLayout = new BorderLayout();
JPanel background = new JPanel(frameLayout);
background.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
buttonPanel = new ButtonPanel(this);
background.add(BorderLayout.SOUTH, buttonPanel.buttonBox);
paintPanel = new PaintPanel(this);
paintPanel.setSize(600, 600);
background.add(BorderLayout.CENTER, paintPanel);
frame.getContentPane().add(background);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}
public class PaintPanel extends JPanel {
public Window window;
public PaintPanel(Window window) {
this.window = window;
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.blue);
g.fillRect(0, 0, 300, 200);
}
}
public class ButtonPanel {
public Window window;
public Box buttonBox;
public JButton startButton;
public JButton resetButton;
public ButtonPanel(Window window) {
this.window = window;
buttonBox = new Box(BoxLayout.X_AXIS);
startButton = new JButton("Start");
startButton.addActionListener(new startButtonListener());
buttonBox.add(startButton);
resetButton = new JButton("Reset");
resetButton.addActionListener(new resetButtonListener());
buttonBox.add(resetButton);
}
}

Try:
paintPanel.setPreferredSize(600, 600);
As Window.pack() sizes to the preferred sizes of its subcomponents and JPanel gets its preferred size from its child components (in your case there are none).

Related

Adding Canvas on JPanel on JFrame

I hope the title wasn´t too confusing.
First off, I am still a beginner, I only started learning java a few months ago and didn´t start working with graphic components until a few weeks ago. Here is my problem:
I got a JFrame as a container and then a Canvas "canvas" to store BufferedImages and JPanel "bPanel" to hold three JButtons. For some reason, even though I used bPanel.setOpaque(false) and/or bPanel.setBackground(new Color(0, 0, 0, 0) the JPanel would still block the Canvas, no matter which one I add first and which one I add second, no matter if I add the Canvas onto the JFrame or the JPanel.
I looked around the internet for hours and tried at least 5 different solutions that did not work. For some reason, I can´t get loading images through a JPanel to work, probably because my display class is not extending anything.
Anyway, let´s continue:
Here is the code I have (yes I know I could go for Display extends JFrame but that doesn´t solve the problem, I already tried that).
public class Display {
private Game game;
private JFrame frame;
private Canvas canvas;
private JPanel bPanel;
private String title;
private int width, height;
private JButton stand, draw, reset;
private Icon drawIMG, standIMG, resetIMG;
private int bwidth, bheight;
public Display(String title, int width, int height) {
this.title = title;
this.width = width;
this.height = height;
createDisplay();
}
private void createDisplay() {
frame = new JFrame(title);
frame.setSize(width, height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
canvas = new Canvas();
canvas.setPreferredSize(new Dimension(width, height));
canvas.setMaximumSize(new Dimension(width, height));
canvas.setMinimumSize(new Dimension(width, height));
canvas.setFocusable(false);
bPanel = new JPanel();
bPanel.setSize(width, height);
bPanel.setPreferredSize(new Dimension(width, height));
//bPanel.setOpaque(false);
bPanel.setBackground(new Color(0, 0, 0, 0));
bPanel.setLayout(null);
drawIMG = new ImageIcon(this.getClass().getResource("/textures/button_draw.png"));
standIMG = new ImageIcon(this.getClass().getResource("/textures/button_stand.png"));
resetIMG = new ImageIcon(this.getClass().getResource("/textures/button_reset.png"));
draw = new JButton(drawIMG);
stand = new JButton(standIMG);
reset = new JButton(resetIMG);
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent drawClicked) {
if (game.getPhase() == 1)
game.playerDraw();
}
});
stand.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent standClicked) {
if (game.getPhase() == 1)
{
game.setPhase(2);
removeButtons();
}
}
});
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent resetClicked) {
game.reset();
}
});
bwidth = 300;
bheight = 100;
//bPanel.add(new JLabel(new ImageIcon(getClass().getResource("/textures/background.png"))), BorderLayout.CENTER);
addButton(draw);
addButton(stand);
addButton(reset);
draw.setBounds(100, ((height/2)-(bheight/2)), bwidth, bheight);
stand.setBounds(((width-100)-bwidth), ((height/2)-(bheight/2)), bwidth, bheight);
reset.setBounds(((width/2)-40), (height-120), 80, 80);
frame.add(canvas);
frame.add(bPanel);
frame.pack();
}
public void showImage(String path) {
JLabel jl = new JLabel();
jl.setIcon(new javax.swing.ImageIcon(getClass().getResource(path)));
frame.add(jl);
frame.repaint();
}
public void setGame(Game game) {
this.game = game;
}
public void addButton(JButton button) {
bPanel.add(button);
bPanel.setLayout(null);
}
public void showButtons() {
draw.setVisible(true);
stand.setVisible(true);
}
public void removeButtons() {
draw.setVisible(false);
stand.setVisible(false);
}
public void removePanel(JPanel panel) {
frame.remove(panel);
}
public Canvas getCanvas() {
return canvas;
}
public JFrame getFrame() {
return frame;
}
public JPanel getBPanel() {
return bPanel;
}
}
In this try of mine for example the picture was only displayed after the JButton "stand" was pressed:
public void showImage(String path) {
JLabel jl = new JLabel();
jl.setIcon(new javax.swing.ImageIcon(getClass().getResource(path)));
frame.add(jl);
frame.repaint();
}
I am pretty desperate bc I spent many hours trying to figure out a solution to load graphics in.
Thanks for help in advance :D
Several issues:
Don't use a null layout. Swing was designed to be using with layout managers.
The default layout manager for a JFrame is a BorderLayout. If you don't specify a constraint, the component is added to the CENTER. Only one component can be displayed in the CENTER. So you can't just keep adding components to the frame. You need to have a parent/child hierarchy.
Don't use a Canvas. That is an AWT component. If you want to do custom painting in Swing you can use the JPanel
So your code might be something like:
//addButton(draw);
//addButton(stand);
//addButton(reset);
bPanel(draw);
bPanel(stand);
bPanel(reset);
The default layout manager for a JPanel is a FlowLayout. So the buttons will be added from left to right and centered on the panel.
//frame.add(bPanel);
frame.add(bPanel, BorderLayout.PAGE_START);
Now all the buttons will be added at the top of the frame.
//private Canvas canvas;
private JPanel canvas;
….
//canvas = new Canvas();
canvas = new JPanel(new BorderLayout());
…
//frame.add(canvas);
frame.add(canvas, BorderLayout.CENTER);
Now the "canvas" will be added to the CENTER of the BorderLayout of the frame, which means it will take up all the space not used by the button panel.
public void showImage(String path) {
JLabel jl = new JLabel();
jl.setIcon(new javax.swing.ImageIcon(getClass().getResource(path)));
frame.add(jl);
frame.repaint();
}
You can't add the image directly to the frame because you have already added other components to the frame. Instead use:
//frame.add(jl);
//frame.repaint();
canvas.add(jl);
canvas.repaint();
So the image is added to the canvas which is added to the frame so you have a parent/child hierarchy.
//draw.setBounds(100, ((height/2)-(bheight/2)), bwidth, bheight);
//stand.setBounds(((width-100)-bwidth), ((height/2)-(bheight/2)), bwidth, bheight);
//reset.setBounds(((width/2)-40), (height-120), 80, 80);
Above code is not needed, since it is the job of the layout manager to set the size and location of each component based on the rules of the layout manager.
So basically you need to start over and learn some Swing basics. Maybe the Swing tutorial will help. There are demos of how to use each of the layout managers.

Java Canvas doesn't resize correctly

I have to programm a game with the exact resolution of 128x128 but the Canvas dont want to match.
public class Window extends Canvas{
private static final long serialVersionUID = 1L;
private JFrame frame;
public Window(BufferedImage icon){
this.setMinimumSize(new Dimension(128, 128));
this.setMaximumSize(new Dimension(128, 128));
this.setPreferredSize(new Dimension(128, 128));
this.setSize(128, 128);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(this, BorderLayout.CENTER);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
if(icon != null){
frame.setIconImage(icon);
}
}}
The size of the Canvas (getWidth(), getHeight) is 134*128 instad of 128*128..
I would think getHeight would return the 134. That would. Be the title of the window. Is the Jframe obviously the bigger portion?
Don't call your class Window. There is an AWT component using that name so it is very confusing. Class names should be more descriptive.
Don't extend Canvas. When using Swing your would extend JComponent or JPanel for custom painting.
Don't create the frame in your class. The frame is not a property of the class and doesn't belong there.
I have to programm a game with the exact resolution of 128x128
Works fine for me on Windows.
Its definetly the getWidth(),
There is a minimum width of the frame. The frame must be able to paint all the buttons in the title bar and the left/right borders.
So I would guess that because you are using a BorderLayout, the frame is being resized to its minimum and then your custom class is resized based on the rules of the layout manager.
So if you want the preferred size of the panel to be respected, try a different layout manager on the frame. For example use a FlowLayout, it will respect the size of any component added to it.
Simple example:
import java.awt.*;
import javax.swing.*;
public class GamePanel extends Canvas
{
public GamePanel()
{
setBackground( Color.RED );
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(128, 128);
}
private static void createAndShowGUI()
{
GamePanel panel = new GamePanel();
JFrame frame = new JFrame("GamePanel");
frame.setLayout( new FlowLayout() );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel);
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
System.out.println(panel.getSize());
System.out.println(frame.getSize());
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}

JPanel won't move if it fills the screen

First of all, sorry for the vague title I don't know how to word the question in a sentence.
I have a simple programme that slides one JPanel into view as another gets pushed out, when a button is clicked.
If the first JPanel's width is set as getWidth() then the JPanel will not move when the button is clicked, however if I change the width to getWidth() - 1 it works perfectly fine!?!
A simple example is shown below
public class SlidingJPanel extends JFrame{
public JPanel panel = new JPanel();
public JPanel panel2 = new JPanel();
public JLabel label = new JLabel(" SUCCESS!!!!!!!");
public JButton button = new JButton("TESTING");
public class MyJPanel extends JPanel implements ActionListener{
public int x = 0;
public int delay = 70;
final Timer timer = new Timer(delay,this);
public MyJPanel(){};
public void paintComponent(Graphics g)
{
super.paintComponent(g);
button.setBounds(10, 20, 100, 50);
button.addActionListener(this);
panel.setBorder(BorderFactory.createLineBorder(Color.black));
panel.setBounds(x, 0, getWidth(), getHeight());
panel.add(button);
panel2.setBorder(BorderFactory.createLineBorder(Color.blue));
panel2.setBounds(x - getWidth(), 0, getWidth(), getHeight());
panel2.add(label);
add(panel);
add(panel2);
}
#Override
public void actionPerformed(ActionEvent arg0) {
timer.addActionListener(move);
timer.start();
}
ActionListener move = new ActionListener(){
public void actionPerformed(ActionEvent e){
repaint();
x++;
}
};
}
public static void main(String args [])
{
new SlidingJPanel();
}
SlidingJPanel()
{
Container container = getContentPane();
MyJPanel panel = new MyJPanel();
container.add(panel);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(500,500);
setTitle("JPanel Draw Rect Animation");
setVisible(true);
}
}
ignore any coding conventions I may have ignored or missed this is just a rough draft.
Hope someone can help :)
The paintComponent() method is for painting only! There is no need for you to override this method.
You should NOT be:
updating the property of components (ie. bounds, border)
adding components to a container
If you want to animate a component then when the timer fires you can use setLocation(...) or setSize() or setBounds(). The component will automatically be repainted.
I don't know if fixing this will solve your problem, but the current approach is wrong.

Centering Panel in Java

For some reason i am having problems centering my panel vertically that is located inside another panel. I do exactly as the examples i studied but still no luck.
Down there is my code. Despite using setAlignmentY(0.5f) on my container panel, it still wont center when i resize the window.
Also the components inside container panel wont center either, despite setAligenmentX(0.5f).
I wonder if there is a solution for this, I pretty much tried everything out there but couldnt find a solution.
JLabel idLabel;
JLabel passLabel;
JTextField id;
JTextField pass;
JButton enter;
JPanel container;
public JournalLogin()
{
//setLayout(new FlowLayout());
//setPreferredSize(new Dimension(500, 500));
//setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));
container = new JPanel();
container.setLayout(new MigLayout());
container.setAlignmentX(0.5f);
container.setAlignmentY(0.5f);
container.setPreferredSize(new Dimension(300, 300));
container.setBorder(BorderFactory.createTitledBorder("Login"));
add(container);
idLabel = new JLabel("ID:");
idLabel.setAlignmentX(0.5f);
container.add(idLabel);
id = new JTextField();
id.setText("id");
id.setAlignmentX(0.5f);
id.setPreferredSize(new Dimension(80, 20));
container.add(id, "wrap");
setAlignmentX and Y are not the way to go about doing this. One way to center a component in a container is to have the container use GridBagLayout and to add the component without using any GridBagConstraints, a so-called default addition. There are other ways as well.
For example to alter Nick Rippe's example (1+ to him):
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.*;
public class UpdatePane2 extends JPanel {
private static final int PREF_W = 300;
private static final int PREF_H = 200;
public UpdatePane2() {
JPanel innerPanel = new JPanel();
innerPanel.setLayout(new BorderLayout());
innerPanel.add(new JLabel("Hi Mom", SwingConstants.CENTER),
BorderLayout.NORTH);
innerPanel.add(new JButton("Click Me"), BorderLayout.CENTER);
setLayout(new GridBagLayout());
add(innerPanel);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("UpdatePane2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new UpdatePane2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Alignments tend to be pretty picky in Swing - they do [usually] work... but if all you're looking for is a panel that's centered, I'd recommend using Boxes in the BoxLayout (My personal favorite LayoutManager). Here's an example to get you started:
import java.awt.Dimension;
import javax.swing.*;
public class UpdatePane extends JPanel{
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
//Create Buffers
Box verticalBuffer = Box.createVerticalBox();
Box horizontalBuffer = Box.createHorizontalBox();
verticalBuffer.add(Box.createVerticalGlue()); //Top vertical buffer
verticalBuffer.add(horizontalBuffer);
horizontalBuffer.add(Box.createHorizontalGlue()); //Left horizontal buffer
//Add all your content here
Box mainContent = Box.createVerticalBox();
mainContent.add(new JLabel("Hi Mom!"));
mainContent.add(new JButton("Click me"));
horizontalBuffer.add(mainContent);
horizontalBuffer.add(Box.createHorizontalGlue()); //Right horizontal buffer
verticalBuffer.add(Box.createVerticalGlue()); //Bottom vertical buffer
// Other stuff for making the GUI
verticalBuffer.setPreferredSize(new Dimension(300,200));
JFrame frame = new JFrame();
frame.add(verticalBuffer);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
});
}
}
You will need to get the LayoutManager to center the layout for you. Currently it looks like the implementation of "MigLayout" does not honor the Alignment. Try changing it or creating a subclass.

How to absolutely position a JPanel over another JPanel with setLayout(null) and a background image?

I'm working with JFrames in Java, specifically with absolutely positioned elements that need to overlap. I understand that to overlay components, one should make a JPanel (with setOpacity(false);), and position it with either setBounds(x,y,x2,y2); or setPosition(x,y) & setSize(x,y). Unfortunately the panels act like CSS's inline-divs; they take up only the needed amount of room on their line, and do not stack.
This is the code I have so far, but it doesn't seem to act like I'd imagine it would:
class Login extends JFrame {
private JPanel backgroundpanel;
private JPanel panel;
private JPanel panel2;
private JTextField usernameBox;
private JPasswordField passwordBox;
private JButton button;
private int height = 319;
private int width = 452;
private ImageIcon ii = new ImageIcon("special-window-BG.png");
private JLabel image;
public Login() {
setLayout(null);
setTitle("Login");
setSize(width,height);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
buildPanel();
add(backgroundpanel);
setVisible(true);
}
private void buildPanel() {
usernameBox = new JTextField(20);
passwordBox = new JPasswordField(20);
button = new JButton("Login");
image = new JLabel(ii);
backgroundpanel = new JPanel();
panel = new JPanel();
panel2 = new JPanel();
backgroundpanel.add(panel);
backgroundpanel.add(panel2);
backgroundpanel.add(image);
panel.setBackground(Color.red);
panel.setBounds(0, 0, 10, 10);
panel.setOpaque(false);
panel2.setBackground(Color.blue);
panel2.setBounds(0, 0, 10, 10);
panel2.setOpaque(false);
panel.add(passwordBox);
panel2.add(button);
backgroundpanel.setOpaque(false);
backgroundpanel.isOptimizedDrawingEnabled();
backgroundpanel.setBounds(0, 0, width, height);
...cot'd, however unnecessary.
So basically, I'd like to know how to absolutely position JPanels (or JComponents, if that's simpler) over a JPanel with a background-image.
Thanks for taking a look at this question, I've spent far too much time on this method; the commented-out code extends nearly 500 lines passed what I posted, so I have nowhere else to turn to. The image below shows a crude illustration of what I'm trying to accomplish, I'm not sure if I'actually come close to getting it yet, because sometimes the JComponents seem to disappear as if they're behind the background image, however I'd like to find the simple solution that's most likely right in front of my eyes!
http://i.stack.imgur.com/revz8.jpg
I'd like to find the simple solution that's most likely right in front of my eyes!
Something like this?
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class LoginPanel extends JPanel {
private static final long serialVersionUID = 1L;
BufferedImage image;
LoginPanel(BufferedImage image) {
super(new GridBagLayout());
this.image = image;
JPanel controls = new JPanel(new BorderLayout(15,35));
controls.setOpaque(false);
controls.setBorder(new EmptyBorder(110,0,0,0));
JPanel fields = new JPanel(new GridLayout(0,1,30,30));
fields.setOpaque(false);
controls.add(fields, BorderLayout.CENTER);
fields.add(new JTextField(20));
fields.add(new JPasswordField(20));
JPanel button = new JPanel(new GridBagLayout());
button.setOpaque(false);
controls.add(button, BorderLayout.PAGE_END);
button.add(new JButton("Log In"));
Dimension prefSize = new Dimension(image.getWidth(),image.getHeight());
setPreferredSize(prefSize);
add(controls);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://i.stack.imgur.com/revz8.jpg");
final BufferedImage image = ImageIO.read(url);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
LoginPanel p = new LoginPanel(image);
JOptionPane.showMessageDialog(null, p);
}
});
}
}
You're setting setLayout(null) on your JFrame but not on the "backgroundpanel" (which layout is default set to FlowLayout).
You shouldn't set layout of your Login frame - because it is default set to BorderLayout - and it's ok (you want the "backgroundpanel" to grow to match the parent).
Instead setLayout(null) on your JPanel - "backgroundpanel" - to which you add your arbitrary positioned panels.

Categories

Resources