Most layout managers have no-argument constructors (that is, you can create a FlowLayout with new FlowLayout (), a GridLayout with new GridLayout (), a GridBagLayout with new GridBagLayout (), etc.). However, BoxLayout requires that you pass both the container that it will be managing and the axis along which the components should be laid out.
My question is: since you're already telling the layout manager which component to lay out, why do you need to write
BoxLayout bl = new BoxLayout(myPanel, BoxLayout.Y_AXIS);
myPanel.setLayout(bl);
instead of just the first line?
I took a quick look at the BoxLayout source code and saw that the constructor I use (lines 178-185) doesn't make a call to target.setLayout(this) or anything of the sort. It seems like it would be really simple to just add that. Is there a reason why it's not included in the Swing library?
If it matters, I'm using
java version 1.7.0
Java(TM) SE Runtime Environment (build 1.7.0-b147)
on Win7Pro.
Thanks!
SSCCE:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
public class BoxLayoutSSCCE extends JFrame {
// Change this to see what I mean
public static final boolean CALL_SET_LAYOUT = true;
public BoxLayoutSSCCE () {
super("Box Layout SSCCE");
JPanel panel = new JPanel();
BoxLayout bl = new BoxLayout(panel, BoxLayout.Y_AXIS);
if (CALL_SET_LAYOUT) {
panel.setLayout(bl);
}
panel.add(new JButton("Button 1"));
panel.add(new JButton("Button 2"));
}
public static void main (String[] args) {
BoxLayoutSSCCE blsscce = new BoxLayoutSSCCE();
blsscce.pack();
blsscce.setVisible(true);
}
}
The Container must exist before it can be passed to BoxLayout. Typically one writes something like this:
JPanel myPanel = new JPanel();
BoxLayout bl = new BoxLayout(myPanel, BoxLayout.Y_AXIS);
myPanel.setLayout(bl);
It's tempting to combine the last two lines, but the principle of least astonishment suggests that the layout's constructor should not otherwise alter the container's state.
Convenienly, javax.swing.Box provides "A lightweight container that uses a BoxLayout object as its layout manager."
public class Box extends JComponent implements ... {
public Box(int axis) {
super();
super.setLayout(new BoxLayout(this, axis));
}
}
Now a single line will do:
Box myBox = new Box(BoxLayout.Y_AXIS);
BoxLayout makes sure that the layout methods are applied to the correct container. It enforces that the same container that was specified in the constructor is used in various methods, such as layoutContainer(Container target), preferredLayoutSize(Container target), etc. It boils down to the checkContainer() method that does the verification:
void checkContainer(Container target) {
if (this.target != target) {
throw new AWTError("BoxLayout can't be shared");
}
}
BoxLayout implementation probably caches some details about the container and tries to maintain state, so it cannot be shared.
EDIT:
BoxLayout implements LayoutManager2.invalidateLayout() where it does reset its cached details. Other layout implementations follow the same pattern. For example, GroupLayout and OverlayLayout also require container argument in their constructors.
Related
I'm fairly new to JFrame and I want to know why my items are not showing up on the window. I know i dont have a ActionHandler but I just want my textfield's to show up on my window. Here's my code:
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
public class FirstGUI extends JFrame{
public void GUI(){
setTitle("Welcome");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(600,600);
JLabel title = new JLabel();
title.setText("Apple Inc. Member Login Port");
title.setFont(new Font("Arial", Font.PLAIN, 24));
JTextField login = new JTextField("Login",10);
JPasswordField pass = new JPasswordField("Password");
add(title);
add(login);
add(pass);
}
public static void main(String[] args){
FirstGUI a = new FirstGUI();
a.GUI();
}
}
but when i run it i get this:
but when i run it i get this:
You get an empty screen because you add the components to the frame after the frame is visible.
As has already been suggested you need to use an appropriate layout manager. FlowLayout is the easiest to start with.
invoke setVisible(true) AFTER adding the components to the frame.
So the code should be more like:
panel.add(...);
panel.add(...);
add(panel);
pack();
setVisible(true);
I agree to MadProgrammer's suggestions (+1)
Well, lets take a look at your program though
You actually have created a JFrame with components in it. Its working fine as well, but your question of "why are my items not showing up in the JFrame" is not because you did something wrong but because missed out something i.e. revalidate()
Try:
public static void main(String[] args){
FirstGUI a = new FirstGUI();
a.GUI();
a.revalidate();
}
I'm not saying this will give you perfect UI.. what I'm trying to say is this will help you understand the Swing better. Learn about Swing Layout managers and then work on your UI to have better results
revalidate(): This component and all parents above it are marked as needing to be laid out. This means the Layout Manager will try to realign the components. Often used after removing components. It is possible that some really sharp swing people may miss this. I would think that you will only know this if you are actually using Swing.
The default layout manager for JFrame is BorderLayout.
This means that your components are essentially all been added ontop of each other.
Try changing the layout manager to something like FlowLayout (for example)...
Take a look at A Visual Guide to Layout Managers and Using Layout Managers for more details.
Also, avoid setSize where possible, use Window#pack instead
Update
I'd also like to introduce you to Initial Threads which should be used to launch your UI code...
The only one reason :
setVisible(True); method for the frame should be put on the end of the code.
if you give this line on the top of the code that is when you create a frame. This will cause that problem.
Don't add the components directly to your frame. Instead add to the content pane, which is where a JFrame stores all of the components that it draws. Usually this is a JPanel.
Here is an example:
public class GUI
{
private JPanel content;
public void GUI
{
/*Other code*/
content = new JPanel();
add(content); //make content the content pane
content.add(title);
content.add(login);
content.add(pass);
}
If that fails, call setVisible(true) and setEnabled(true) on all of your components.
On a side note you may want to make your GUI function a constructor.
import javax.swing.*;
import java.awt.*;
class Myframec extends JFrame
{
Myframec()
{
Container c = this.getContentPane();
c.setLayout(null);
this.setBounds(10,10,700,500);
this.setTitle("Welcome");
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.setBounds(0,0,700,500);
panel.setBackground(Color.gray);
panel.setLayout(null);
c.add(panel);
Font f = new Font("Arial",Font.BOLD,25);
Font f1 = new Font("Arial",Font.BOLD,20);
JLabel lable = new JLabel();
lable.setBounds(130,10,400,100);
lable.setText("Apple Inc. Member Login Port");
lable.setFont(f);
panel.add(lable);
JTextField login = new JTextField("Login",10);
login.setBounds(120,150,400,30);
login.setFont(f1);
panel.add(login);
JPasswordField pass =new JPasswordField("Password");
pass.setBounds(120,200,400,30);
pass.setFont(f1);
lable.setFont(f);
panel.add(pass);
c.setVisible(true);
this.setVisible(true);
}
public static void main(String[] argm)
{
Myframec frame = new Myframec();
frame.setVisible(true);
}
}
I can't make the pack() method work. I tried several things. My code looks like this at the moment:
Class 1:
public static void main( String[] args )
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
JavaGui mygui = new JavaGui();
// mygui.setSize(1154, 753);
mygui.setVisible(true);
mygui.pack();
Class 2:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
getContentPane().setLayout(null);
..
getContentPane().add(panelLeft);
...
getContentPane().add(panelRight);
I tried putting the pack method in everywhere, but it's not going to work with this way of adding gui elements. Any suggestions why? I also tried adding everything to a JFrame instead of the getContentPane(), but I can't make that work either.
Don't use null layouts together with pack(). The pack method tells the layout managers and components to size themselves optimally, and if you instead use null layouts, then the gui risks shrinking to a minimal size, since there is no layout to hold it together.
Don't use null layouts at all for the most part. Using these risk your creating rigid GUI's that are almost impossible to extend, improve, debug.
Don't use setSize(...) and pack(). The layouts mostly respect the preferred sizes of components, not their sizes.
Instead:
Use a pleasing and sensible combination of nested JPanels, each using its own layout manager.
Let the components and the layout managers size themselves.
Then pack should help.
The general order that I do is to add all components to the GUI, then call pack(), then setLocationByPlatform(true) (I think), then setVisible(true).
For better help, please check out the Swing Layout Manager Tutorials.
Here are a couple examples to other questions on this site that use various layout managers:
A combination of BorderLayout and GridLayout to create a calculator
BorderLayout and BoxLayout Combination for labels and JTextFields
Using GridBagLayout to create flexible label/textfield grid
I would recommened beginners on building up swing guis to use a good ide with a builtin gui designer like eclipse and windowbuilder or netbeans with matisse. It will help you building up a prototype of your desired gui and gives you an insight how the layouting is done in the source code.
Experiment with the differenet layouts and what is happening when some values are changed.
one does not simply build up a well behaving gui without understanding how the layout works, so doing the recommended tutorials and looking at examples as already posted by Hovercraft Full Of Eels is absolutely necessary.
For your case i just guess what you were up to. Because youre mentioning left and right panels i suggest a JSplitPane which let you divide your screen in two areas which are customizable in size and orientation.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
public class JavaGui extends JFrame {
//SerialVersionId http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
//Calls to Gui Code must happen on the event dispatch thread that the gui does not get stuck
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new JavaGui().setVisible(true);
}
});
}
public JavaGui() {
// Set the desired size of the frame to determine the maximum size of its components
setPreferredSize(new Dimension(1024, 768));
// Set the default close operation, if press x on frame, destroy the frame and exit the application - others are just destroy the frame or just hide the
// frame
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// BorderLayout because we just need a centric gui with one component, here JSplitPane in full size
getContentPane().setLayout(new BorderLayout(0, 0));
// JsplitPane is a bit special as it depends on the divider location between both panels, for the sake of a small example we take the default -1,
JSplitPane splitPane = new JSplitPane();
// 0.5 divides extra space equally to left and right component when resizing the frame - so specifiying a size for the left and right component is not
// necessary
// use the divider location default -1 to let the width of the left component decide where the right component begins, in that case because of the
// resize weight half and half
splitPane.setDividerLocation(-1);
splitPane.setResizeWeight(0.5);
getContentPane().add(splitPane, BorderLayout.CENTER);
// For the panels the same layout as default as the intention is not stated in your question
JPanel leftPanel = new JPanel();
splitPane.setLeftComponent(leftPanel);
leftPanel.setLayout(new BorderLayout(0, 0));
JPanel rightPanel = new JPanel();
splitPane.setRightComponent(rightPanel);
rightPanel.setLayout(new BorderLayout(0, 0));
// Add a button Panel to the south for doing something - flow layout for letting the components flow to the right side
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
// Close Button for closing the frame
JButton btnExit = new JButton("Destroy this frame, but let application run");
btnExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
buttonPanel.add(btnExit);
// Set every component to its preferred size
pack();
// Make it visible
setVisible(true);
}
}
If you want your JFrame to work with a null layout, rearrange your code so that it looks like this:
public class JavaGui extends javax.swing.JFrame
{
public JavaGui()
{
setMinimumSize(1154, 753); // Make sure you do setMinimumSize() instead of setSize() when using pack() so that the JFrame does not shrink to 0 size
setLayout(null);
add(panelLeft);
add(panelRight);
pack();
}
// Next is main method
Main:
public static void main(String[] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
new JavaGui().setVisible(true);
// Do not do any formatting for your JFrame here
}
});
Before, you were modifying the JFrame after it was set visible, so that usually does not work, except for pack(). All components and settings for your JFrame should not be in the main method if you are using an anonymous inner class.
You can also use other layouts. Null layouts are for getting pixels in precise locations, which is used for advanced GUI design such as creating a custom GUI, but it seems that you are making a generic GUI with JPanels. For this, I would recommend using a GridBagLayout, which keeps everything centered if the frame is resized and is easy to use. To use a GridBagLayout, you have to replace setLayout(null); with setLayout(new GridBagLayout()); and set GridBagConstraints. Here is some example code of making a panel with a component and a GridBagLayout:
JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
//natural height, maximum width
c.fill = GridBagConstraints.HORIZONTAL;
}
//For each component to be added to this container:
//...Create the component...
//...Set instance variables in the GridBagConstraints instance...
pane.add(theComponent, c);
// Source: Oracle Docs
When I learned creating Java GUI:s in my first Java course, I was taught to create my windows as JFrame instances, and then add a JPanel to each JFrame and finally add all the GUI components to the JPanel:
class Example extends JFrame {
Example() {
JPanel panel = new JPanel();
this.add(panel);
// Create components here and add them to panel
// Perhaps also change the layoutmanager of panel
this.pack();
this.setVisibility(true);
}
public static void main(String[] args) {
new Example();
}
}
I always though "well, this smells a little; I don't like creating an extra object just to be a container," but I didn't know any other way to do it so I just went on with it. Until recently, when I stumbled over this "pattern":
class AnotherExample extends JFrame {
AnotherExample() {
Container pane = this.getContentPane();
// Add components to and change layout of pane instead
this.pack();
this.setVisibility(true);
}
public static void main(String[] args) {
new AnotherExample();
}
}
Still being quite new to Java, I feel better about the second approach just because it doesn't involve creating a JPanel just to wrap the other components. But what are the real differences between the approaches, except from that? Does any one of them have any great benefits over the other?
I prefer to create a JPanel (which, being a Swing container, can have a border) and set it as the content pane.
To get a JComponent out of the content pane requires casting, which has an even worse smell than creating an extra component.
How set text field location in Java GUI?
I tried this:
public Apletas()
{
inputLine.setLocation(null);
inputLine.setLocation(80, 80);
add(inputLine);
}
But not working.
First, set the layout of your applet to null:
...
public void init()
{
setLayout(null);
}
...
Ignore setLocation()/setBounds() & most especially setLayout(null). Abandon all hope (& the last remnants of sanity), ye' who enter there.
Set locations of components using layout managers.
For sizing of components, it is usually sufficient to provide the appropriate arguments in the constructor (e.g. new JTextArea(rows, columns)), or in some cases, using layout constraints (e.g BorderLayout.CENTER).
For spacing between components, look into both the javax.swing.border package and arguments to the constructor of layout managers, or in some cases, layout constraints (e.g GridBagLayout & GridBagConstraints).
Example:
//<applet code='Apletas' width='600' height='400'></applet>
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class Apletas extends JApplet {
private JTextField inputLine;
public Apletas()
{
inputLine = new JTextField(20);
JPanel mainGui = new JPanel(new BorderLayout(20,20));
mainGui.setBorder(new EmptyBorder(80,80,80,80));
mainGui.add(inputLine, BorderLayout.NORTH);
mainGui.add(new JScrollPane(new JTextArea(20,10)), BorderLayout.CENTER);
JTree tree = new JTree();
tree.expandRow(2);
mainGui.add(new JScrollPane(tree), BorderLayout.WEST);
setContentPane(mainGui);
validate();
}
}
To compile & run
prompt> javac Apletas.java
prompt> appletviewer Apletas.java
See also
Laying Out Components Within a Container & How to Use Borders in the Java Tutorial.
I'm not sure why you would .setLocation(null) on a text field and then set it again.
Is inputLine declared elsewhere (in which case post the full code here please) or are you missing a line like
TextField inputLine = new TextField("Hello world");
You have to null the default layout as by using the code
setLayout(null);
and then can use the setBounds method to locate your swings
it goes like this
JTextField jt = new JTextField();
jt.setBounds(x,y,width,height);
I want to make an object I can add to my java swing application.
The object when instantiated would contain an image and 2 labels - is there a way to do this using java swing?
If there is - can you point me at an example.
I.e i want
Myobj icon = new MyObj(pic, label , label);
window.addComponent(icon);
Cheers
Andy
Create a class MyObj and let it extend JPanel. In the constructor of MyObj you call setLayout(new BorderLayout()) or whatever layout you prefer. Then do for instance add(pic, BorderLayout.NORTH); add(label1, BorderLayout.WEST); add(label2, BorderLayout.EAST);.
Then you should be able to do window.add(new MyObj(pic, label1, label2)).
import java.awt.*;
class MyObj extends JPanel {
public MyComponent(ImageIcon pic, String label1, String label2) {
setLayout(new BorderLayout());
add(new JLabel(label1), BorderLayout.NORTH);
add(new JLabel(pic), BorderLayout.CENTER);
add(new JLabel(label2), BorderLayout.SOUTH);
}
}
public class FrameTest {
public static void main(String[] args) {
JFrame jf = new JFrame("Demo");
jf.add(new MyObj(new ImageIcon("duke.jpg"), "Label 1", "Label 2"));
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
}
Produces
This would typically be done by sublcassing JPanel and, in the constructor creating 3 labels (1 for the image) and adding them to the panel using a suitable layout manager.
Something like this ?
image with two labels http://img297.imageshack.us/img297/5223/capturadepantalla201005i.png
I created a subclass of JPanel and in its constructor I layout the components so it can be used exactly as you thought:
ImageAndLabels demo = new ImageAndLabels("image.png", "labelOne", "labelTwo");
window.add( demo );
Here's the complete source code for this window. May help you to get started.
import javax.swing.*;
import java.awt.Font;
public class ImageAndLabels extends JPanel {
public static void main( String [] args ) {
JFrame frame = new JFrame("image and labels");
frame.add( new ImageAndLabels("./logo.png", // logo
"Grouping swing objects", // label 1
"<html>Hey.<br>" // label 2
+"I want to make an object I can add to my java swing application.<br>"
+"The object when instantiated would contain an image and 2 labels - "
+"is there a way to do this using java swing?</html>") );
frame.pack();
frame.setVisible( true );
}
public ImageAndLabels( String imageURL, String textOne, String textTwo ) {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add( new JLabel( new ImageIcon(imageURL )));
add( new JLabel( textOne ){{
setFont( new Font("Arial", Font.BOLD, 20));
}});
add( new JLabel( textTwo ));
}
}
You can add multiple Swing components to some container component - usually JPanel:
JPanel panel = new JPanel(new SomeLayoutYouLike());
panel.add(..);
panel.add(..);
Read the section from the Swing tutorial on Using Layout Managers. Use the appropriate layout manager to layout the components as you wish. Then add the compnents to a JPanel.
well, the main point of swing is to avoid instantiate your objects with parameters...
for example: (rather not do that unless this vars are imperative to the creation of the object)
MyFrame(Object o1, Object o2...)
for serialization purposes, you would rather use an empty constructor, and to set the external values form out side of the frame(in this case), this way you would never get things mixed up... and avoid much NullPointerException debugging, later on if you would use serialization.
if you want to design components, you should use NetBeans, very simple, very user friendly, allows you to align and locate you labels, as for the ImagePanel.. I had one but I converted it to a scaling image panel.. with scaled layers over it.
If you need, I'll post it here.
Hope this helps,
Adam.