public class MyFrame extends JFrame
{
public MyFrame(String title)
{
setSize(200, 200);
setTitle(Integer.toString(super.getSize().width));
setLayout(new FlowLayout());
for (int i = 0; i < 5; ++i)
{
JButton b = new JButton();
b.setSize(90,50);
b.setText(Integer.toString(b.getSize().width));
this.add(b);![alt text][1]
}
this.setVisible(true);
}
}
why if having button widht 90 I'm getting window where three buttons are in one row instead of two?
FlowLayout will lay out Components left-to-right (or right-to-left) wrapping them if required. If you wish to explicitly set the size of each JButton you should use setPreferredSize rather than setSize as layout managers typically make use of the minimum, preferred and maximum sizes when performing a layout.
Size properties are quite confusing - There is an interesting article here. In particular, note:
Are the size properties always
honored?
Some layout managers, such as
GridLayout, completely ignore the size
properties.
FlowLayout, attempts to honor both
dimensions of preferredSize, and
possibly has no need to honor either
minimumSize or maximumSize.
The FlowLayout just places component one beside the other in a left-to-right order. When the width reaches the one of the container that has that layout it simply wraps on the other line.
If you want to arrange them in a grid-style layout (like it seems you want) you can use the GridLayout that allows you to specify the number of columns and rows:
component.setLayout(new GridLayout(2,2))
The only downside of GridLayout is that every cell of the grid will be of the same size (which is usually good if you just have JButtons or JLabels but when you mix things it will be visually bad).
If you really need more power go with the GridBagLayout, very customizable but with a steeper learning curve at the beginning.
Probably your size problem is related to the fact that you are using setSize but in Swing these things have strange behaviours, you should try by setting setPreferredSize(200,200) instead of setSize. But don't ask me why!
NOTE: you should ALWAYS refer to the frame's content pane and not to the frame it self. When you set layout you should do getContentPane().setLayout(..), when you add items you should do getContentPane().add(..) and so on.
Errata: now every JFrame add, remove, setLayout automatically forward to the content pane.
For one thing, you're not using JFrame correctly: you don't add components directly to the frame, you add them to a JPanel that you then pass to the frame with setContentPane().
Also: it's not very elegant to directly subclass JFrame just to add components. Instead, create your frame as a separate object.
Related
I have a JFrame and a few JPanels that are displayed depending on where the user goes (to login, homepage etc), is there a way to set all JPanels to the same size without having to manually specify?
public mainApp() {
main = new JFrame("Application");
main.setSize(640, 480);
main.setVisible(true);
}
private JPanel loginScreen() {
login = new JPanel(null);
login.setSize(640, 480);
login.setVisible(true);
}
For example I have 5 different JPanels, and have to specify the size in each, is there a way to set a default size that is the same as the JFrame?
You have different valid options here. Knowing that you want to change from one view to another and keep the same size, it sounds like the best option would be to use a CardLayout allowing you to change between the different views without having to worry about repainting and revalidating stuff (this tutorial from Oracle helped me a lot back when I was learning to use this layout: Oracle - How to use CardLayout.
However, as usual with Swing/AWT this is not the only valid option. For example, you could also use the BorderLayout that is applied by default to the ContentPane from the JFrame and add the desired JPanel to the Center of that BorderLayout. However, you would have to manage the view-changing process in this case.
I am trying to create a new JTextField in my Jframe. I want to play around with the positioning of the textfield. I tried using setBounds and setLocation to change the position of the text box but it doesn't change the location of the text box at all.
This is my code:
public class GUI_Tutorial extends JFrame {
public static void main(String[] args) {
GUI_Tutorial frame = new GUI_Tutorial();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setSize(1000, 800);
frame.setVisible(true);
frame.setTitle("Calculator");
frame.setLayout(new FlowLayout());
}
public GUI_Tutorial() {
//frame.setLayout(new FlowLayout());
JTextField num1;
num1 = new JTextField(10);
add(num1);
num1.setVisible(true);
num1.setLocation(5, 5);
}
}
May I know what am I doing wrong?
Your problem is one of layout managers. When you add a component to a container, the layout manager will dictate where the component will go. A JFrame's contentPane (the JFrame sub-container that holds its components) uses a BorderLayout by default, and items added to this container in a default manner will fill the central portion of the container, will fill it completely if nothing else is added to other BorderLayout locations.
Possible solutions for placing items:
The worst suggestion, use a null layout. You would do this by calling getContentPane().setLayout(null);. But when you do this, you the programmer are 100% responsible for the exact position and size of all components added. This leads to hard to maintain GUI's that might not even work on other platforms -- so don't do this.
Use a GUI builder to help build your program: this has definite advantages, one being that it shields you from having to directly understand the layout managers, but this also is a disadvantage, because once you run into edge cases (and you usually will and quickly) that knowledge is essential. My own view is to initially avoid using this so as to better understand the layout managers, and then once you're familiar with the managers, then sure use this as needed.
Better is to learn and experiment with the layout managers, and Borders, playing with placement of components. Remember that you can nest containers (usually JPanels) and each can be given its own layout manager, allowing for complex GUI's that are created easily and maintained easily.
Note that
(again) JFrames (actually their contentPanes), JDialogs, and other top-level windows use BorderLayout by default, that JPanels use FlowLayout by default, that JScrollPanes use their own specialty layout, one that you will likely never want to muck with, and that most other components/containers, such as JComponent, JLabel,... have null layouts by default.
Best resource is the tutorial: Lesson: Laying Out Components Within a Container
Borders can also be useful here, especially the EmptyBorder which can allow you to put a blank buffer zone around your components.
Start playing with the simpler layout managers, FlowLayout, BorderLayout, GridLayout, then BoxLayout, before moving to the more complex
Try removing frame.setLayout(new FlowLayout());. You'll then need to use num1.setBounds(x, y, width, height) rather than setLocation()
But, as other users have pointed out, you should be using a layout manager. Read up on the different layouts and choose the best one for your GUI.
So I have been coding Java for a few months now, and I have been using JOptionPane to display text and variables in my games. I want to upgrade to a single window like a normal game, but I want to only focus on simple buttons and text on the screen. I have tried learning JFrame and ActionListsner, but I failed to completley figure it out. JFrame really confused me.
My question is this: Is there an easier way beside JFrame to just have a window that I can have simple text, buttons and TextFields without the hassle of opening a bunch of windows with JOptionPane, making crap loads of ActionListeners with JFrame or having to get into GUI? If not, where can I find help on how to make games with JFrame?
You should be using a JFrame. Trust me, they aren't that hard to use. Using a JFrame, you could create multiple panels and switch between them using CardLayout.
Since you said you aren't sure about how JFrame works, I gave a short introduction to them at the end of this post. But first, lets first talk about how to solve your problem.
Switching Panels via CardLayout
When you want to switch whats being viewed in the window completely, you're gonna want an entirely different panel for that specific purpose (for example, one for Main Menu, and one for the game). CardLayout was created for this.
You specify a "deck" panel. This panel will hold all the other panels you wanna switch between (cards):
CardLayout layout = new CardLayout();
JPanel deck = new JPanel();
deck.setLayout(layout);
You'll need to maintain a reference to the layout (via a variable) so you can switch between panels.
Now that we have a "deck" panel, we need to add some cards. We do this by creating more JPanels, and specifying a name when we add it to the frame (constraints):
JPanel firstCard = new JPanel();
JPanel secondCard = new JPanel();
deck.add(firstCard, "first");
deck.add(secondCard, "second");
The first card added to the deck will always be the first one to show.
Once you have all your cards added, you can switch between them by calling layout.show(deck, "second");. This is how you use CardLayout to manage multiple panels within your container.
Creating listeners
There's no easier way to manage it. It only gets harder from there (bindings - I highly suggest you look into them). For listeners, there are 2 steps:
Create the listener
Add it to the component
Could be 1 if you created the listener "on the fly" using a lambda:
JButton button = new JButton();
//Java 8+
button.addActionListener(event -> {
//whenever you click the button, code in here triggers
});
For those who don't use Java 8, you will need to use an anonymous class:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//whenever you click the button, code in here triggers
}
});
Only some components support ActionListener. Anything that extends AbstractButton, like JMenuItem, JButton, JRadioButton, and more will support ActionListeners. JTextField also supports it, even though it's not an AbstractButton. Every component supports KeyListener, though, which can also be used to listen for input events from the user.
If you have any questions about this, let me know and I'll be glad to answer them.
Using Swing Components
JFrame
You initialize a JFrame:
JFrame frame = new JFrame();
You then want to set the defaultCloseOperation, to determine what happens when the window closes:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
There are different options, but EXIT_ON_CLOSE will terminate your program after closing the window. If you do not set the defaultCloseOperation, then your window will close, but your program will still be running. BIG NO NO. If you don't want the entire program to exit when you close the frame, use DISPOSE_ON_CLOSE.
After you have the close operation, you might be tempted to set the size. This is bad practice. Your frame should size depending on what's inside of it. To do this, you add your components to your frame, then call pack():
JButton button = new JButton("Button");
frame.add(button);
//add other components
frame.pack();
This will ensure your frame sizes to fit what's inside of it. You should always design GUI from the inside out, to ensure you always have enough room for what you need.
Containers
JFrame is a Container. Containers are just components that hold other components. All containers should have a LayoutManager to manage how components are laid out and, if needed, sized. JFrame isn't the only container though. JPanel is actually a container that's meant to be added to another container (or window). You can add things to a JPanel, then add that panel to another container. This is how you keep things neatly organized. Containers are pretty straight forward, so there's not much to talk about.
Content Pane
When you create a JFrame, it comes along with something called the contentPane. It is a JPanel nested within the JFrame. When you do frame.add(button), you'll notice that add actually refers to the contentPane:
//in Container class
public Component add(Component comp) {
addImpl(comp, null, -1); //where it's added
return comp;
}
//In JFrame class (overriding)
protected void addImpl(Component comp, Object constraints, int index) {
if(isRootPaneCheckingEnabled()) {
getContentPane().add(comp, constraints, index); //adds to content pane instead
} else {
super.addImpl(comp, constraints, index); //if root panes were not supported, which they currently are
}
}
You too can grab the contentPane from the frame using
Container pane = frame.getContentPane();
The reason why the contentPane is in Container form is to ensure a strong API (if JPanels were no longer going to be used for this, we wouldn't need to worry about changing the method type of getContentPane). Although, since it IS a JPanel, you can cast it back to it's original form:
JPanel pane = (JPanel) frame.getContentPane();
Although it's usually not needed. Using it as a Container tends to work just fine.
Layouts
JFrame, by default, uses BorderLayout, but you can change this by calling the setLayout method of the container:
FlowLayout layout = new FlowLayout();
frame.setLayout(layout);
Before jumping into layouts, I want to mention that JPanels use FlowLayout as default, except for the frame's default contentPane, which you can also change by doing frame.setContentPane(...). The contentPane uses BorderLayout as default.
Now, lets talk about a couple, starting with the JFrame default: BorderLayout.
Some layouts require what are called constraints, which tell the layout how to handle that specific component that's being added. You specify these constraints when you add the component to the container:
frame.add(button, BorderLayout.SOUTH);
BorderLayout is pretty simple. You have 5 options: NORTH, SOUTH, EAST, WEST, or CENTER. (there are other values such as PAGE_START, which are interchangeable. It's best to just use the easier form)
All constraints for BorderLayout are static field variables that you call similar to how I did. BorderLayout is actually an easy layout to use, seeing how there's not much to it. Even though it's simplicity limits what you can do (you can only put it in a certain position like NORTH), you'd usually combine layouts to get the result you want. This layout can be very powerful when combined with other layouts.
FlowLayout is pretty straight forward, as well as other layouts out there. One of the less straight-forward ones would be GridBagLayout, which can be a really flexible layout. It can also be pretty complex, though, as the documentation even states.
When using GridBagLayout, you need to create a constraints object: GridBagConstraints. To work with GridBagLayout, you set the constraints using the constraints object, then add both the component and the constraints to the container:
frame.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("yoyoyo");
frame.add(button, gbc);
Even if we don't adjust the constraints, we MUST still specify it when adding a component to the container.
Lets say we had 4 buttons, and we wanted to put them side by side. You would adjust the constraint's gridx and gridy values:
JButton button = new JButton("1");
JButton button2 = new JButton("2");
JButton button3 = new JButton("3");
JButton button4 = new JButton("4");
frame.add(button, gbc);
gbc.gridx = 1; //or gridx += 1, or gridx = 1. gridx starts at 0
frame.add(button2, gbc);
gbc.gridx = 0; //must reset the value back to normal
gbc.gridy = 1;
frame.add(button3, gbc);
gbc.gridx = 1;
//since gridy already == 1, no need to change it
frame.add(button4, gbc);
We can use the same constraints object, as long as we reset values when needed.
GridBagLayout starts centered, and works from the center out, unless you specify otherwise. You cannot skip grid spaces either. Also, as you'll notice, all your buttons will be touching. If you wanted a little space between each component, you can set the insets of the constraints:
int top = 5, left = 5, bottom = 1, right = 1;
gbc.insets.set(top, left, bottom, right);
There is a LOT more to this layout, and sadly I just don't feel this is the best place to give the explanation to it all, seeing how it's already documented (I even added the link).
There are many other layouts out there. Get familiar with as many as you possibly can, then find the ones that'll help suit your needs. Positioning should ALWAYS rely on the LayoutManager that's being used. As for sizing, that kinda depends on the layout you're using.
I would highly recommend using JavaFX. It's a very nice fairly easy to use GUI system with nice looking and customizable controls. JavaFX events (basically ActionListeners) are pretty straightforward as well.
This should get you started: http://docs.oracle.com/javase/8/javafx/get-started-tutorial/get_start_apps.htm#JFXST804
I believe CardLayout is what you're looking for. With it, the programmer can choose which JPanels should be visible in the JFrame. Upon user interaction you can switch the contents of the JFrame without new JFrames or JOptionPanes popping up.
Is there an easier way than using Swing for simple games? Swing has a learning curve, but with the right resources and practice you can learn to build simple GUI applications pretty quickly.
Colleagues.
I'm trying to construct simple GUI in Java, where JFrame has Border Layout. I want to put JScrollPane with JTable to CENTER, and JPanel without layout to NORTH.
The problem is that JPanel doesn't visible. There is simple examle of the problem:
JFrame frame = new JFrame("Test frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Test button");
button.setBounds(10, 10, 40, 20);
JPanel panelN = new JPanel(null); // layout = null, panelN without layout
panelN.add(button);
frame.add(panelN, BorderLayout.NORTH);
JTable table = new JTable(new DefaultTableModel(4, 4));
JScrollPane scrollPane = new JScrollPane(table);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setSize(400, 400);
frame.setVisible(true);
You have to use a LayoutManager. It's totally discouraged not using layoutManager, but if you want this you have to set panel.setBounds(..) to the panel too.
By default JPanel has FlowLayout so if you put
JPanel panelN = new JPanel(); // FlowLayout used
panelN.add(button);
frame.add(panelN, BorderLayout.NORTH);
So your frame will look like this.
Layout Managers determines the size and position of the components within a container. Although components can provide size and alignment hints, a container's layout manager has the final say on the size and position of the components within the container.
It's strongly recommended cause for example if you have to resizes components or show in differentes resolutions you delegate this work to layout managers
I don't know the expected behavior of a null layout, but without further requirements you might as well just instantiate with the zero-arg constructor:
new JPanel();
If you didn't set any layout to the panel, when adding components the panel don't know where to put the component, so baisicly the component don't show until you set a specific location for components one by one by component.setBounds(x,y,width,hieght) method.
Note that it's not a good practice to remove the layout manager because of the different platformes, suppose that your program working on Window and MacOS and Linux, you'v better to use the layout managers instead.
Take a look at this post also and see #Andrew Thompson's comment on my answer:
Java GUIs might have to work on a number of platforms, on different
screen resolutions & using different PLAFs. As such they are not
conducive to exact placement of components. For a robust GUI, instead
use layout managers, or combinations of them, along with layout
padding & borders for white space, to organize the components.
After all:
If you have a requirement or an assignment telling you you must use absolute layout, then use it, otherwise avoid it.
It is OK to use containers with no layout manager because you actually CAN set container's layout to NULL. And it's a nice idea to position your components with setBounds(). But in this case, you just have to consider your container. What size it need to be? A layout manager would calculate this for you, and if you don't have one, you have to set the size of your panel by yourself, according to components you have added to it.
As pointed by others here, the case it that the border-layout manager of your frame needs the preferred size of your NORTH panel (actually, the preferred height). And you have to set it, or values will be zeros and the container will become invisible. Note that for the CENTER panel this is not needed as it gets all space possible.
I had a problem like yours before and have written a fast function to resize a container according to bounds of a given component. It will be as large as needed to show this component, so dimension (w,h) and position (x,y) are considered. There's an "auto-resize" version that can be used once, after all components are added.
public static void updatePreferredSize(Container cont, Component comp) {
int w = cont.getPreferredSize().width;
int h = cont.getPreferredSize().height;
int W = comp.getBounds().x + comp.getBounds().width;
int H = comp.getBounds().y + comp.getBounds().height;
if (W>w||H>h) cont.setPreferredSize(new Dimension(W>w?W:w, H>h?H:h));
}
public static void autoPreferredSize(Container cont) {
for (Component comp : cont.getComponents())
updatePreferredSize(cont, comp);
}
You can use updatePreferredSize() after adding every component to a panel, or use autoPreferredSize() once, after all addings.
// [...]
panelN.add(button);
updatePreferredSize(panelN, button);
// [...]
// or...
// [...]
autoPreferredSize(panelN);
// [...]
frame.setVisible(true);
This way, if you do not set you north panel height with a fixed value, with help of these functions you can expect your button will be visible according the position you set it with setBounds().
I have a panel with flow layout, and it can contain a variable number of items - from 1 to 2000. I want to put it inside a scroll pane, scrollable in vertical direction, and with fixed width. The problem is, when I set preferred size of panel to something like (800,600), some items are missing, and there is no scroll. If I set up preferred size of scroll pane, then all elements in flow pane are put on one very long line.
Setting maximum size on any element seems to do nothing at all - layout managers ignore it.
How can I fix this?
I want to put it inside a scroll pane, scrollable in vertical direction, and with fixed width
You can use the Wrap Layout for this.
Don't set the preferred size of the panel. But you can set the preferred size of the scroll pane so the frame.pack() method will work.
You could use BoxLayout to do this:
JPanel verticalPane = new JPanel();
verticalPane.setLayout(new BoxLayout(verticalPane, BoxLayout.Y_AXIS));
JScrollPane pane = new JScrollPane(verticalPane);
//add what you want to verticalPane
verticalPane.add(new JButton("foo"));
verticalPane.add(new JButton("bar"));
This of course will use the preferred size of each component added. If you want to modify the preferred size for example of a JPanel, extend it and override getPreferredSize:
class MyPanel extends JPanel(){
public Dimension getPreferredSize(){
return new Dimension(100,100);
}
}
A note: BoxLayout will take in consideration getPreferredSize, other LayoutManager may not.
Please criticize my answer, I'm not sure it's completely correct and I'm curious to hear objections in order to know if I understood the problem.