I have a JPanel that contains a bunch of small icons, and a JLabel containing a bigger icon.
First of all, I need clarification on something. By not setting panel.isOpaque, the said panel would have a transparent background and would properly overlap the icons of my JPanel and JLabel. Is this true?
Now to my problem. I've been following the LayeredPanes tutorial on the Oracle website, and I can't seem to make it right on my case.
JFrame window = new JFrame();
ImageIcon underIcon;
URL urlUnder = myClass.class.getResource("images/underImage.gif");
underIcon = new ImageIcon(urlUnder);
JLabel labelUnder = new JLabel(underIcon);
ImageIcon panelIcon;
URL urlAbove = myClass.class.getResource("images/aboveImage.gif");
panelIcon = new ImageIcon(urlAbove);
JLabel aboveIcon1 = new JLabel(panelIcon);
JLabel aboveIcon2 = new JLabel(panelIcon);
JPanel panelAbove = new JPanel(new BorderLayout());
panelAbove.setOpaque(false);
panelAbove.add(aboveIcon1, BorderLayout.WEST);
panelAbove.add(aboveIcon2, BorderLayout.EAST);
JLayeredPane layeredPane = new JLayeredPane();
layeredPane.setLayout(new BorderLayout());
layeredPane.add(labelUnder, BorderLayout.CENTER, 1);
layeredPane.add(panelAbove, BorderLayout.CENTER, 2);
layeredPane.setOpaque(true);
window.setContentPane(layeredPane);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
I only see the contents of the panel. And I'm 100% sure the images are there on getResource, so that's not the issue. Please advice on how to properly use Layered Panes.
The problem is you are setting a layout manager to the JLayeredPane
layeredPane.setLayout(new BorderLayout());
In of it self, this is not a problem, but your choice of layout manager is.
BorderLayout will only a single componet to occupy any of it's 5 predefined positions. This means when you add your second component, it, effectively, replaces the first.
Try using someone like GridBagLayout instead. Providing a single GridBagConstaint set so that the weightx/y values ara equal to 1, the fill property is set to BOTH and it's gridx/y properties are set to 0
Unlike BorderLayout, GridBagLayout will allow you to layout components to the same position
From the Java Tutorial:
layeredPane.add(dukeLabel, new Integer(2), 0);
This code uses the three-argument version of the add method. The third argument specifies the Duke label position within its depth, which determines the component's relationship with other components at the same depth.
Positions are specified with an int between -1 and (n - 1), where n is the number of components at the depth. Unlike layer numbers, the smaller the position number, the higher the component within its depth. Using -1 is the same as using n - 1; it indicates the bottom-most position. Using 0 specifies that the component should be in the top-most position within its depth. As the following figure shows, with the exception of -1, a lower position number indicates a higher position within a depth.
So your depth orders are the wrong way around, also start from 0.
I also found this in the Solving Common Component Problems Section
Problem: The components in my layered pane are not layered correctly. In fact, the layers seem to be inversed — the lower the depth the higher the component.
This can happen if you use an int instead of an Integer when adding components to a layered pane. To see what happens, in the LayeredPaneDemo class, change
layeredPane.add(label, new Integer(i));
to
layeredPane.add(label, i);.
Related
I'm using Java and MiGLayout to try and re-create this layout:
http://www.methvin.com/splitter/3csplitter.html
So something like this:
Each column needs to be resizable
The size of the left and right columns stay the same size when resizing the main window
Middle column fills all available space and changes size when resizing
In other words, the left and right panel need to "stick" to the left and right side of the window, but also be resizable (and not change proportionally when being resized)
I've tried many things, but the resizing is always the problem. Here is the current code which is an attempt at doing this with a nested JSplitPane.
public class MainGUI extends JFrame {
private String app_name = "Layout Test";
private int window_x_min = 700;
private int window_y_min = 450;
public MainGUI() {
setTitle(app_name);
setSize(window_x_min + 200, window_y_min + 100);
setMinimumSize(new Dimension(window_x_min, window_y_min));
setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
JPanel panel = new JPanel();
this.setContentPane(panel);
panel.setLayout(new MigLayout("","[]","[grow]"));
JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitpane.setContinuousLayout(true);
splitpane.setTopComponent(new JButton("middle"));
splitpane.setBottomComponent(new JButton("right"));
JSplitPane splitpane2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitpane2.setContinuousLayout(true);
splitpane.setDividerLocation(450);
splitpane2.setBottomComponent(splitpane);
splitpane2.setTopComponent(new JButton("left"));
panel.add(splitpane2, "push, grow");
this.pack();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
I've managed to make it load up initially how I like, however with the following issues:
Resizing from the right side changes size of the the "right" panel, not the size of the middle panel
Resizing from the left side changes the size of the "right" panel as well
It appears that the left side of the app is working, I need the right side to function the same way. Hope that I'm making sense, thanks!
You need to manage how the space is allocated when the split panes are resized. This is done by using the setResizeWeight() method. By default the value is 0.0f which means the "left" component is fixed. So you need to manipulate this property on one of the split panes. Read the API for more information on how this property works.
I don't use MigLayout, but doing a simple test using a standard BorderLayout all you need to add is:
splitpane.setResizeWeight(1.0f);
Also, when you post a SSCCE don't forget to include the main() method so we can just copy/paste/execute the code. We should not need to do any extra work.
I want to reduce the size between the components with in the Formatting group (left side on the image). How to do this?
JPanel formattingGroup = createGroupWithName("Formatting");
formattingGroup.setMinimumSize(new Dimension(250, 20));
formattingGroup.setLayout(new GridLayout(5, 0));
add(formattingGroup);
final JCheckBox showSurface = new JCheckBox("Show surface");
showSurface.setSelected(true);
formattingGroup.add(showSurface);
final JCheckBox showTerrain = new JCheckBox("Show terrain");
showTerrain.setSelected(true);
formattingGroup.add(showTerrain);
final JCheckBox showVehicleStatus = new JCheckBox("Show vehicle status");
showVehicleStatus.setSelected(true);
formattingGroup.add(showVehicleStatus);
JPanel pnl = createGroupWithName("Depth Stretch");
formattingGroup.add(pnl);
JSlider slider = new JSlider(0, 10);
pnl.add(slider);
When using a GridLayout all components are made the same size.
You are adding a JPanel with a TitledBorder and a JSlider to the grid. Therefore the checkboxes will take the same vertical height as that panel.
You need to use a different layout manager for the panel. Maybe a vertical BoxLayout.
You might look at available size variants, discussed in Resizing a Component.
Use gridbaglayout because that gives you the opportunity to give weights, to columns or rows and set spacing and padding values.
I made a Swing application that contains out of 12 Frames and they all are made with GridBagLayout.
I also tried other before that but they all had limits. That's where the GridBagLayout kicks in. It's a bit harder in begin to understand how it works, but once you get feeling with it, it really is best thing to get the components where you want.
If you want i'll give you a cool example of a frame created with GridBagLayout.
I am trying to add components to a jpanel dynamically from a database, one after the other, however they extend beyond the limits of the form (and even the screen) horizontally. I am using a panel with BoxLayout which positions inner components on the x-axis. What I would like to do is limit the addition of components on the x-axis at the border of the form (or the border of the container panel) .
What I tried so far is:
To set the maximum size (width) of the
container jpanel but that did not
work.
To set the layout manager to
flowlayout, but it also expands
infinitely on the x-axis.
To position the container panel inside a
scrollpane. That makes a huge
horizontal scrollbar, which means that
it does not limit the components to
the border of the form.
Should I make a custom layout? Any ideas simpler than that?
My code looks like:
jpanelCases.setLayout(new BoxLayout(this.jpanelCases, BoxLayout.X_AXIS));
db = Database.getInstance();
List<Category> cats = db.getCategories();
for(Category c : cats){
JPanel jp = new JPanel();
//addition of other components to the newly created panel here
jpanelCases.add(jp);
}
however they extend beyond the limits of the form (and even the screen) horizontally.
So what do you want to do in this case:
a) just ignore the components and not add them to the frame
b) display the components on a new line
If the answer is "b", the try the WrapLayout.
Try to put 2 glues, one at first one at last:
jpanelCases.setLayout(new BoxLayout(this.jpanelCases, BoxLayout.X_AXIS));
jpanelCases.add(Box.createHorizontalGlue());
db = Database.getInstance();
List<Category> cats = db.getCategories();
for(Category c : cats){
JPanel jp = new JPanel();
//addition of other components to the newly created panel here
jpanelCases.add(jp);
}
jpanelCases.add(Box.createHorizontalGlue());
What about using JScrollPane on the JPanel? This will contain the elements within the panel without exceeding its physical boundaries. Otherwise, if you do not like the scroll idea I suggest that you somehow keep track of the total sum of width of elements added to the panel with respect to the panel physical width, and stop adding elements when the next element width added to the sum exceeds the panel width.
I hope this helps!
BorderLayout does something strange. If I add two panels to a Container with the same constraint (BorderLayout.CENTER for instance), then the first one goes away, even if the second one is deleted or made invisible
It seems as though it would make sense for it to "stack" each element on top of the previous ones.
Is this correct and by design? If so, is there some documentation on it?
Has anyone else been frustrated by it? Have you a solution, such as a custom LayoutManager?
Sample code:
JFrame frame = new JFrame();
frame.setSize(500, 500);
JPanel panel1 = new JPanel();
panel1.setBackground(Color.blue);
frame.getContentPane().add(panel1);
JPanel panel2 = new JPanel();
panel2.setBackground(Color.red);
frame.getContentPane().add(panel2);
panel2.setVisible(false); // Seems like it should allow us to see panel1.
frame.setVisible(true);
This creates and displays a 500x500 blank box.
BorderLayout was simply not designed to do what you want. Separation of responsibility. If you want that behavior you should compose: combine the BorderLayout with a CardLayout. Though for the actual stack behavior, you'll have to code something yourself (or find someone who already has.)
Is this correct and by design?
Yes.
You need to understand the basics of how layout managers work. One of the jobs of the layout manager is to set the "location" and "size" of the components added to the panel. In the case of a BorderLayout it only tracks 5 components so only the last component added to the CENTER is known by the layout manager.
Layout management is not done when components are added to the panel. It is done when the frame is packed, or made visible (or the revalidate() method is invoked) . In this case the blue panel is not part of the components managed by the BorderLayout so its size remains (0, 0), which means there is nothing to paint.
Try changing your code to:
JPanel panel1 = new JPanel();
panel1.setSize(200, 200);
and you will see the blue panel painted at the specified size.
Now try commenting out:
//panel2.setVisible(false);
and you will see both panels. This is because as components are added to the panel they are assigned a ZOrder. Basically the last component added is painted first, which is why the blue panel is painted on top of the red panel. Check out the setComponentZOrder() method of the Container class for more information.
The CardLayout is probably the layout manager you should be using, but you can check out the Overlap Layout as well.
JPanel pMeasure = new JPanel();
....
JLabel economy = new JLabel("Economy");
JLabel regularity = new JLabel("Regularity");
pMeasure.add(economy);
pMeasure.add(regularity);
...
When I run the code above I get this output:
Economy Regularity
How can I get this output, where each JLabel starts on a new line? Thanks
Economy
Regularity
You'll want to play around with layout managers to control the positioning and sizing of the controls in your JPanel. Layout managers are responsible for placing controls, determining where they go, how big they are, how much space is between them, what happens when you resize the window, etc.
There are oodles of different layout managers each of which allows you to layout controls in different ways. The default layout manager is FlowLayout, which as you've seen simply places components next to each other left to right. That's the simplest. Some other common layout managers are:
GridLayout - arranges components in a rectangular grid with equal-size rows and columns
BorderLayout - has one main component in the center and up to four surrounding components above, below, to the left, and to the right.
GridBagLayout - the Big Bertha of all the built-in layout managers, it is the most flexible but also the most complicated to use.
You could, for example, use a BoxLayout to layout the labels.
BoxLayout either stacks its components on top of each other or places them in a row — your choice. You might think of it as a version of FlowLayout, but with greater functionality. Here is a picture of an application that demonstrates using BoxLayout to display a centered column of components:
An example of code using BoxLayout would be:
JPanel pMeasure = new JPanel();
....
JLabel economy = new JLabel("Economy");
JLabel regularity = new JLabel("Regularity");
pMeasure.setLayout(new BoxLayout(pMeasure, BoxLayout.Y_AXIS));
pMeasure.add(economy);
pMeasure.add(regularity);
...
I read this piece of code:
pMeasure.setLayout(new BoxLayout(pMeasure, BoxLayout.VERTICAL));
It seems BoxLayout doesn't have VERTICAL. Upon searching, this will work using the following code:
pMeasure.setLayout(new BoxLayout(pMeasure, BoxLayout.Y_AXIS));
Here is what you need to use:
JLabel economy = new JLabel("<html>Economy<br>Regularity</html>");
A quick way is to use html within the JLabel.
For instance include the <br/> tag.
Otherwise, implement a BoxLayout.
Make a separate JPanel for each line, and set the dimensions to fit each word:
JLabel wordlabel = new JLabel("Word");
JPanel word1 = new JPanel();
word1.setPreferredSize(new Dimension(#,#);
This should work for each word. You can then add each of those JPanels to your main JPanel. This also allows you to add other components next to each word.