I'm currently trying to fill a JPanel (using GridLayout) with Buttons. These Buttons should be squares having a set width.
The programm should be able to resize the playing field (n*m buttons). Problem is, the buttons are automatically stretched to completely fill the JScrollpane, completely ignoring the set height and width. Thus making the scrollbars unnecessary.
Here is my code:
Creating the panels:
panel_game = new javax.swing.JPanel();
jScrollPane1 = new javax.swing.JScrollPane(panel_game);
Trying to resize the JPanel: (where x and y are the number of buttons and zoomlvl the size)
panel_game.setLayout(new java.awt.GridLayout(x, y));
panel_game.setBounds(panel_game.getX()+5,panel_game.getY()+5,x*zoomlvl-5,y*zoomlvl);
panel_game.setPreferredSize(new Dimension(x*zoomlvl,y*zoomlvl));
And adding the buttons: (f is my playfield, a 2D-Array)(buttons is a Arraylist, containing my buttons)
for(int i=0; i<f.getSize()[0]*f.getSize()[1];i++){
buttons.add(new JButton());
buttons.get(i).setSize(zoomlvl, zoomlvl);
buttons.get(i).setPreferredSize(new Dimension(zoomlvl,zoomlvl));
panel_game.add(buttons.get(i));
}
I have no clue how I can tell Java to stop resizing my buttons etc. automatically.
Help would be appreciated :)
GridLayout is built to fill the container that uses it with components. If the container holding the buttons is constrained in size, then the buttons will stretch or fill to fill that container as closely as possible. A solution may be to nest layout managers so that this does not occur, but I can't give more specific advice without more code, particularly a minimal example program. Images might help too.
If you want to prevent the "button grid panel" from stretching to fill the whole viewport area of the ScrollPane, you could place this grid panel into another panel (with FlowLayout).
In general, you should either
not set the preferred size of the panel_game OR
not set the preferred size of the buttons
because
when you set the preferred size of the buttons, then the preferred size of the panel will be computed from the preferred sizes of the buttons
when you set the preferred size of the panel, then the size of the buttons will be determined by the size of the panel, respectively
Maybe this is what you want to achieve:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollButtonGrid
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int x = 5;
int y = 5;
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(x, y));
for(int i=0; i<x*y;i++){
JButton button = new JButton(String.valueOf(i));
button.setPreferredSize(new Dimension(100,100));
panel.add(button);
}
JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER, 0,0));
container.add(panel);
JScrollPane scrollPane = new JScrollPane(container);
f.getContentPane().add(scrollPane);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Related
How can I use MigLayout so that after pack() I can see a JFrame with a proper size to hold all its children components, with borders, insets and gaps?? Now I see some elements cut off, leaving half of the size visible but half cut off.
I just figured out how to guarantee the proper size of a Container according to the sum of size of all the contained components without hardcoding anything.
Create a JPanel panel as your working panel, instead of touching the contentPane. Just add it back to the contentPane. Don't touch the contentPane, it is the key.
Set the layout of panel without hardcoded row height, column width, etc. This may ruin the layout, because your hardcoded height may be lesser or more of it is needed, leaving some line with wrong size, and leave your last line/column half cut off.
Add your elements into panel. When adding them you can specify sizes.
Add panel back to contentPane: getContentPane().add(panel); We don't need to set the layout of contentPane.
At last, pack(), setVisible(true) as you wish. No need to setSize(), setBounds(), etc. The insets and gaps will be handled automatically by MigLayout. Viola!
A SSCCE:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
public class InsetsAndBorder extends JFrame {
public InsetsAndBorder() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setLayout(new MigLayout("insets 2 2 2 2, fillx, debug", "3[]3[]3[]3", "5[]5[]5[]5"));
JLabel label1 = new JLabel("1");
JLabel label2 = new JLabel("2");
JButton button = new JButton("No way!");
panel.add(label1, "cell 1 2, grow");
panel.add(label2, "cell 2 2, grow");
panel.add(button, "cell 0 1, grow");
getContentPane().add(panel);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
InsetsAndBorder frame = new InsetsAndBorder();
}
});
}
}
I am currently reading chapter 12 of Head First Java about making GUIs. They have just mentioned that JFrames are split into center, north, south, east and west. The book then uses the 2 argument add() method to add specified components to a specified region with that JFrame.
I can add a JButton to each of the five regions fine. I can also add my own JPanel to the center region with JButtons all around it. But then when I try to add a JPanel to any region other than center, the JPanel does not appear.
I really have searched all over the web and Stack Overflow for the past hour and I have not come across anything that mentions adding JPanels to any region other than center in a JFrame. So my question is: is it possible to add JPanels to the north, south, east or west regions of a JFrame?
Thanks in advance to anyone that can help me with this.
Here is the code that I've been trying to run with my JPanel in the north region:
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.BorderLayout;
public class StackQ {
JFrame frame;
public static void main(String [] args) {
StackQ gui = new StackQ();
gui.go();
}
public void go() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("location test");
JButton button2 = new JButton("location test");
JButton button3 = new JButton("location test");
JButton button4 = new JButton("location test");
myDrawPanel custom = new myDrawPanel();
frame.getContentPane().add(button, BorderLayout.CENTER);
frame.getContentPane().add(button2, BorderLayout.EAST);
frame.getContentPane().add(button3, BorderLayout.WEST);
frame.getContentPane().add(button4, BorderLayout.SOUTH);
frame.getContentPane().add(custom, BorderLayout.NORTH);
frame.setSize(300,300);
frame.setVisible(true);
}
}
class myDrawPanel extends JPanel {
public void paintComponent(Graphics g) {
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
Color random = new Color(red, green, blue);
g.setColor(random);
g.fillOval(20,20,100,100);
}
}
It could be clearer but this is described in the BorderLayout documentation:
The components are laid out according to their preferred sizes and the constraints of the container's size.... the CENTER component may stretch both horizontally and vertically to fill any space left over.
Put another way, the CENTER component will be stretched (if needed) to fill the application, and any other components need to specify a preferred-size in order to take some of that from the CENTER.
JButton specifies a preferred size by default, based on the contents of the button. JPanel on the other hand does not - its preferred size depends on its contents, and your JPanel doesn't have any contents, therefore its preferred size is zero.
In short, specify a preferred size for your JPanel and the BorderLayout will try to allocate at least that much space for the panel.
Just for any potential future viewers of this post, to get the desired result, I first imported the Dimension class at the very top of my StackQ class (this is needed because the setPreferredSize() method used later on in the go() method accepts an argument of type Dimension):
import java.awt.Dimension;
And then I added this code to the go() method immediately after the instantiation of the myDrawPanel class:
Dimension dims = new Dimension(1366, 200);
custom.setPreferredSize(dims);
I chose 1366 as the width because that's how big my screen is.
Thanks everyone for your help!
Don't know if I'm making a rookie mistake, but changing the row parameter when creating a GridLayout for a JPanel in my JFrame, seems to be causing another JPanel to vanish altogether:
Here's the stripped down version of the code:
In the GridBug constructor I set up my layout and put a sub class of JPanel in BorderLayout.CENTER. This does other stuff in my original code, but here just draws a box to show it's being displayed.
Somehow the state of the bottom panel which is added to BorderLayout.PAGE_END causes the center panel to vanish
In particular, changing the GridLayout row parameters to a higher value causes the center panel to vanish, lower values work fine
The code as it is now, does not work on my computer, if I uncomment some of the code to reduce row parameters, or if I don't add the JLabel or subPanels then it works...
I have no idea what I'm doing wrong:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class GridBug extends JFrame{
static class ImagePanel extends JPanel{
#Override
public Dimension getPreferredSize(){
return new Dimension(200,200);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
System.out.println("Painting image panel...");
g.setColor(Color.CYAN);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
public GridBug() {
setLayout(new BorderLayout());
/*PROBLEM CODE HERE*/
//add center image panel - does not appear depending on GridLayout settings in other panels
ImagePanel centerPanel = new ImagePanel();
add(centerPanel , BorderLayout.CENTER);
//add bottom panel
JPanel bottomPanel = new JPanel();
add(bottomPanel, BorderLayout.PAGE_END);
bottomPanel.setLayout(new GridLayout(6,0)); //doesn't work
// bottomPanel.setLayout(new GridLayout(5,0)); //works
JPanel subPanel = new JPanel();
//if I pass more than 4 or so rows as param to gridlayout,
//then imagePanel is not displayed
subPanel.setLayout(new GridLayout(4,0)); //doesn't work
// subPanel.setLayout(new GridLayout(3, 0)); //works
//if I don't add this label - works
JLabel label = new JLabel("A Label:");
subPanel.add(label);
bottomPanel.add(subPanel); //if I don't add the subPanel it works fine
/*END OF PROBLEM CODE?*/
//set window params
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setLocationRelativeTo(null);
setVisible(true);
}
public static final void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GridBug();
}
});
}
}
Not sure the exact result you want, so I didn't even try. But the problem you're facing, is one of the reasons, you want to pack() your frame, and not setSize(). You're constricting the preferred sizes of the component. pack() respects the preferred size of all your components, and should be used, rather than setSize()
bottomPanel.setLayout(new GridLayout(6,0)); //doesn't work..
// with pack(), now it does.
pack();
//setSize(400,400); // if you increase the size it'll work also, but just pack()
You just need to work on the laying out of your components now to get your desired look :)
A more detailed explanation of your problem.
This is how you code currently looks when I set the background. Note: you can already see the setSize() is taking a toll on your top panel's preferred size (200, 200).
The top CYAN is your image panel.
The BLUE is the subPanel with 4 row. With GridLayout, all the rows will be at least the size of it largest component. In this case it's the label. You can see the blue area is 4 times the height of the label (as it should be)
Not the RED, which is the bottomPanel. This has 5 rows. The largest component is the subPanel, so the total size of the bottomPanel is the size of the subPanel x 5, as you can also see. Once you add another row, the top panel gets pushed out.
I have the following little program:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ScrollPanePlay extends JFrame
{
public static void main(String[] args)
{
ScrollPanePlay frame = new ScrollPanePlay();
frame.setVisible(true);
}
public ScrollPanePlay()
{
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JPanel panel = new JPanel(this);
panel.setLayout(new FlowLayout(FlowLayout.LEFT));
panel.add(new JLabel("one"));
panel.add(new JLabel("two"));
panel.add(new JLabel("three"));
panel.add(new JLabel("four"));
panel.add(new JLabel("five"));
JScrollPane scrollPane = new JScrollPane(panel);
add(panel, BorderLayout.CENTER); // <== add either panel or scrollpane
pack();
}
}
If I add panel, then the labels in the panel wrap when the window is made narrow, as expected for FlowLayout.
If I add the panel to scrollpane and add the scrollpane to the frame, then the labels don't wrap, because the scrollPane is reporting sufficient width (I guess) to hold all the labels, and allows the user to scroll to see them.
I would like the scrolling behavior, but only for vertical - I would like the user to choose the width he wants to see, and have the scrollbar appear only for scrolling vertically. How can I do that?
I tried extending JScrollPane and returning true from getScrollableTracksViewportWidth(), but, as I expected, that doesn't do what I want because it's the frame's width I want things to track, not the viewport. I tried extending JPanel and overriding getWidth to return the width of the frame, but that still left all the labels in a horizontal row, i.e., they quit wrapping. Is there something that will do this without a custom layout manager? Seems to me all we need is programmatic control of the viewport width.
It is a viewport issue. When inside a JScrollPane a JPanel wont resize to fit the JScrollPane size (that is kind of why ScrollPanes exist).
You can use a Scrollable Panel instead of a JPanel and set it to fit the maximum width (in this case it would fit to the JScrollPane).
Something like:
ScrollablePanel panel = new ScrollablePanel( new BorderLayout());
panel.setScrollableWidth( ScrollablePanel.ScrollableSizeHint.FIT );
Check this answer to a little more general answer and for a link to the .class.
I have this simple Java Swing test application that show an upper label and under this label a button:
package com.techub.exeute;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import org.jdesktop.application.SingleFrameApplication;
public class Main extends SingleFrameApplication{
public static void main(String[] args) {
Main a = new Main();
a.startup();
}
#Override
protected void startup() {
JFrame frame = new JFrame("FrameDemo");
frame.setMinimumSize(new Dimension(800, 400));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel myLabel = new JLabel("Hello World !!!", SwingConstants.CENTER);
myLabel.setFont(new Font("Serif", Font.BOLD, 22));
myLabel.setBackground(Color.RED);
myLabel.setOpaque(true);
myLabel.setPreferredSize(new Dimension(100, 80));
frame.getContentPane().add(myLabel, BorderLayout.NORTH);
Button myButton = new Button("Click Me !!!");
myButton.setMaximumSize(new Dimension(50, 25));
frame.getContentPane().add(myButton, BorderLayout.CENTER);
//Display the window.
frame.pack();
frame.setVisible(true);
}
}
The button is in the BorderLayout.CENTER position. The problem is that this button occupies all available space in the CENTER position also if I have set the Maximum Size property with a Dimension object.
What am I wrong? What can I do to create a button having specific dimension?
You're currently telling Java to place the button in the center of your BorderLayout. Java then thinks that you want the entire area filled with this button. If you want to place a normal sized button in the center of your BorderLayout, add the button to a new JPanel and place the JPanel inside BorderLayout.CENTER.
Doing this, you're telling Java to fill out BorderLayout.CENTER with your JPanel. The elements that you place inside this JPanel will appear normal, because these elements are not getting "stretched" because of your BorderLayout - the JPanel is.
You fail to realize that it's not only the max/min/preferred sizes of the components that determine the outcome. The LayoutManager has a lot to say in this, and as you noticed, BorderLayout will fill the whole area with the component.
What you can do is create a JPanel, make it use for example FlowLayout and put that JPanel to BorderLayout.CENTER and the JButton to the JPanel.
You are using BorderLayout, North region has JLabel. JButton is put on center. JButton will occupy all the space left as per BorderLayout.
Please find more information about Layout on link : http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
U are using BorderLayout, button occupy all the left space of the frame.
use setBounds() function .
myButton.setBounds(10,200,100,30);