Why there are too much layering in the Container in the swing? - java

Can anybody tell me what is the difference between the below Code
JFrame jf=new JFrame();
JButton jb=new JButton();
jf.add(jb);
and
JFrame jf=new JFrame();
Container c=jf.getContentPane();
JButton jb=new JButton();
c.add(jb);
Even I am not Clear about the RootPane,LayeredPane,GlassPane. What is the use of RootPane?? I have never used it in my coding. I have read it from the below link
http://docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
but not too much clear because there is no practical situation where I have used the above.
Thanks for your answer!!

As of Java 5, there is no difference between you code examples. JFrame#add is now redirected to JFrame#getContentPane on your behalf.
Before Java 5, it would throw an exception, which meant you had to add your components directly to the content pane yourself.
The JRootPane is used to provide a light weight container onto which other Swing components can be added. More importantly, it provides a means by which the top level Swing containers can organise themselves.
The JRootPane is made of layers. At the bottom is the JMenuBar and content pane and above that is the glass pane.
Because of the way it's constructed, the JRootPane can actually have a number of additional layers (typically between the content pane and the glass pane), this is typically used for things like popups.
The glass pane acts as a overlay which can be used to render content over the top of everything else (as well as block mouse and keyboard events).
Take a look at How to use Root Panes.
Normally, other then the content pane and glass pane, you wouldn't typically use any of the other parts of the root pane.
You could also take a look at this for a quick example of a glass pane in use

Both are same...
Both are added in java.awt.Container object.
JFrame and Container have following hierarchy
Case 1: JFrame#add()--> Frame#add()--> Window#add()--> Container#add() // Component add to container
Case 2: JFrame#getContentPane() --> getRootPane().getContentPane(); it will return Container object(Container#add)

Related

Container (AWT) Object in Java

I am researching about AWT as Frame, JFrame and I saw object Contianer as parent of them but I wonder that can I use container object as Frame or JFrame, below is my code but it's not working:
public class icontainer {
public static void main(String[] args) {
Container icon= new Container(); // new JFRAME();
icon.setSize(300,300);
icon.setLocation(300, 300);
icon.setVisible(true);
}
}
Why don't we use icon = new Container() instead JFrame?
That is not how inheritance works.
A Container is a base class that does not much more but keeping track of its children. And that is it. Its not a window itself.
From the Javadoc:
A generic Abstract Window Toolkit(AWT) container object is a component that can contain other AWT components.
Components added to a container are tracked in a list. The order of the list will define the components' front-to-back stacking order within the container. If no index is specified when adding a component to a container, it will be added to the end of the list (and hence to the bottom of the stacking order).
But that is it. There is no mention of a window anywhere. It doesn't display anything on its own. It is displayed in something else.
A JFFrame on the other hand is a window (it literally inherits from java.awt.Window via java.awt.Frame).
From the Javadoc of java.awt.Frame:
A Frame is a top-level window with a title and a border.
And then there are some details about how the Frame layouts the contents etc.pp, and how the window decorations should look like and behave, especially wrt. WindowEvents like WINDOW_CLOSING. Those are things that are all only present in Frames, not Containers.
And if you go down to JFrame, you get some specialized handling of e.g. the content pane to make it work better with the JFC/Swing component architecture.
Container window = new Container();
This is code you can write, but nearly useless. In order for the container to actually be displayed on screen, it still needs to be put into a window.
Container window = new JFrame();
You can do that, because JFrame is a subclass of container, and thus is assignment-compatible to Container.
However, you lose the ability to call any methods introduced by java.awt.Window, java.awt.Frame or javax.swing.JFrame on the container -- because those don't exist on Container.
You should use a JFrame or Frame, because a Container of its own is not a "Window" that is displayed to the User.
I do not recommend it, but you could store your JFrame or Frame as a Container like this:
Container icon = new JFrame();
icon.setSize(300,300);
icon.setLocation(300, 300);
icon.setVisible(true);

Do I need JPanel always?

I am now writing code simple GUI that's for start the game window. I only need Do you want to start game message and start button on the window. But I have a confusing concepts for the JFrame and JPanel. Actually, I thought I need to add JPanel to JFrame to add the other components such as JLabel, JButton,...etc. But I realized I don't actually need JPanel. I can just add the components simply use add(button), add(label) to JFrame. So why I need JPanel. And I think JFrame doesn't need JPanel but JPanel need JFrame. Am I understand correctly?
No, not always. A simple graphical user interface may be implemented by just adding components "directly" to a JFrame. But in order to get more flexibility, you would always use JPanels. For example, to employ different layouts in different parts of the GUI, to group certain components together, etc.
A JFrame is backed by a JRootPane, a part of which is a contentPane.
(image from Oracle Javadoc)
When you add components to a JFrame, you are really adding them to the content pane, e.g.: frame.getContentPane().add(Component).
A JFrame is a common starting scene of a Swing GUI application, while a JPanel is intended to be put in another scene (container). Since both content pane and a JPanel inherit from the same class (Container) you may use them in a similar manner, as far as adding components to them goes.
Do I need JPanel always?
No. Well, unless you need a Swing GUI. Then yes.
Another answer replied words to the effect. "No, you can add components direct to a frame" What they missed was that components added to a JFrame are added to the content pane (automatically). The content pane is a JPanel.
Having said that:
I (and many others) would recommend designing an app based around a main content panel, then adding that panel to a top-level container as needed. The top level container might be a JFrame, JWindow, JDialog, JOptionPane ..
What prompted the question? A JPanel is a very 'light weight' container (in more ways than one). A GUI can contain 1000s and not be burdened by doing so. Of course, that's a rare requirement, but just saying .. use panels as needed and don't worry about it.

Java: Upgrading to a single window

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.

The JPanel contentpane confusion

I am learning Java Swing and I appended a menuBar to the frame. By default this should call jframe.getContentPane().add(child). When I ran the script the menuBar didn't show up. But the button was at the very top "y=0" if that makes sense.
Then I realized my mistake I actually had to put in a menu in the menubar. Then the menuBar showed up. So that got me thinking...is the "menubar" "contentpane" actually 2 panels? It is confusing the hell out of me. Because that acted a lot like a panel. But getContentPane() returns a Container, not a JPanel object so I'm confused.
If so, does that mean that the only thing that is dumped directly into a frame are just Jpanel objects? Hence JButtons, JLabels are not directly in a frame...
Does that mean, jpanels are "nested"?
One more thing that is confusing me. If a jpanel can control how things are positioned, what is a LayoutManager for? :S
Thanks, and please answer as if to a 2yr old asking why the sky is blue,ha ;)
Some random thoughts:
Yes, JPanels and other components are often "nested". This is where a firm understanding of the Swing/AWT layout managers is essential.
While the type returned by a JFrame's getContentPane() is technically a Container, it's also a JPanel (which inherits eventually from Container).
I believe that you can make anything that derives from Container the contentPane, but you need to take care that it is opaque.
for that there is method
frame.setJMenuBar(menuBar);
for todays Java Swing GUI, is not neccesary declare ContentPane, from Java5, and with BorderLayout as default LayoutManager
then frame.add(myPanel);
//is same as frame.add(myPanel, BorderLayout.CENTER) and occupated whole Container
basic stuff about how to use LayourManagers
getContentPane() always returns a Container instance. However, you should note that JPanel objects are Container instances, as well as other classes in the Swing framework. The actual class of the instance returned is irrelevant, as you do not have control over which implementation of Container is used as a contentPane (unless you have forced a specific contentPane), and most of the time this should not be a problem.
You can add many GUI widgets in a JFrame, such as JButton, JLabel, etc. However, they will be automatically added to the associated contentPane.
JPanel does not handle the objects positioning, the LayoutManager associated with your panel does; either automatically based on its own set of rules (e.g. FlowLayout), or by using the constraints you have specified when adding the object to the container (the GridBagLayout layout manager is a good example). The JavaDoc on the LayoutManagers usually contain enough information to get you started on using them.
You can have nested panels, yes. A Container can contain other Container instances. While it seems to be a complicated solution, it does enable you to control exactly how your GUI is displayed. Depending on which LayoutManager you are using, on the needs you have to fulfill with your user interface, and on your own preferences/habits as a developper, you might need less or more nested panels.
You need to see this API doc for JComponent.
If you look at the inheritance hierarchy, you will see that the JComponent extends Component so a JComponent is a Component.
Also under Direct Known Subclasses, you can see the list of all the classes that extend the JComponent including JMenuBar and JPanel.
So, JMenuBar and JPanel are two more specialized versions of JComponent (or Container).

JAVA: Ways to fill a Frame. add(), setContentPane(), getContentPane()

I found three ways to fill my JFrame frame = new JFrame("...")
createContentPanel returns a JPanel and createToolBar returns a ToolBar.
frame.add(this.createToolBar(), BorderLayout.PAGE_START); //this works and puts the ToolBar above and the ContentPanel under it<br>
frame.add(this.createContentPanel(), BorderLayout.CENTER);
frame.setContentPane(this.createContentPanel()); //this lets the JToolBar hover over the ContentPanel
frame.getContentPane().add(this.createToolBar());
frame.getContentPane().add(this.createContentPanel()); //this only puts the last one into the JFrame
frame.getContentPane().add(this.createToolBar());
And now I am wondering why should i use the getContentPane()/setContentPane() method if i could just use a simple frame.add(...) to fill my frame.
You are right that it doesn't matter which you use (JFrame#add(...) vs. JFrame#getContentPane().add(...)) since they both essentially call the same code, however there will be times in the future when you'll need access to the contentPane itself, such as if you want to change its border, set its background color or determine its dimensions, and so you'll likely use getContentPane() at some point, and thus getting to know it and be familiar with it would be helpful.
//this only puts the last one into the JFrame
You need to understand how layout managers work. The default content pane is a JPanel that uses a BorderLayout. When you add a component and don't specify a constraint, then it defaults to the CENTER. However you can only has a single component in the center so the layout manager only knows about the last one added. When the layout manager is invoked it sets the size() and location() of that component. The other component has a size of 0, so it is never painted.
In Java 1.6, you can just use the add method of JFrame:
http://download.oracle.com/javase/6/docs/api/javax/swing/JFrame.html
(It will be delegated to the contentPane.)
http://download.oracle.com/javase/1.4.2/docs/api/javax/swing/JFrame.html
Which says:
The JFrame class is slightly
incompatible with Frame. Like all
other JFC/Swing top-level containers,
a JFrame contains a JRootPane as its
only child. The content pane provided
by the root pane should, as a rule,
contain all the non-menu components
displayed by the JFrame. This is
different from the AWT Frame case. For
example, to add a child to an AWT
frame you'd write:
frame.add(child);
However using JFrame you need to add the child
to the JFrame's content pane instead:
frame.getContentPane().add(child);
The same is true for setting layout
managers, removing components, listing
children, and so on. All these methods
should normally be sent to the content
pane instead of the JFrame itself. The
content pane will always be non-null.
Attempting to set it to null will
cause the JFrame to throw an
exception. The default content pane
will have a BorderLayout manager set
on it.

Categories