JLayeredPane not respecting layers - java

I have a JLayeredPane. My program works something like this:
JPanel p1 = new JPanel(new BoxLayout(p1, BoxLayout.X_AXIS));
JPanel p2 = new JPanel(new BoxLayout(p2, BoxLayout.Y_AXIS));
JLayeredPane lp = new JLayeredPane();
lp.add(p1, 1);
lp.add(p2, 0);
Both p1 and p2 have components like buttons, etc...
The issue is that when I add both JPanels to the JLayeredPane, NOTHING appears.
I tried changing the layout of the JLayeredPane().
For example, I did:
lp.setLayout(new BoxLayout(lp, BoxLayout.X_AXIS));
Then, the JPanels do show, but they are shown adjacent, not respecting the layers of the JLayeredPane.
Am I forced to use a null layout?
How can I make my JLayeredPane respect the layers and show my two BoxLayout JPanels correctly?
When I give my JLayeredPane a layout, it shows the panels, but it is not respecting the layers at all.

You need a layout manager which understands the Z-Axis. The default layout managers don't understand the Z-Axis of the JLayeredPane.
If you simply want to overlay stuff on top of each other you can use a LayoutManager like this:
JLayeredPane layeredFooPane = new JLayeredPane();
// The magic!
layeredFooPane.setLayout(new LayeredPaneLayout(layeredPane));
// Add components:
layeredFooPane.add(fooComponent, new Integer(JLayeredPane.DEFAULT_LAYER + 10));
layeredFooPane.add(barComponent, JLayeredPane.DEFAULT_LAYER);
LayoutManager Class:
public class LayeredPaneLayout implements LayoutManager {
private final Container target;
private static final Dimension preferredSize = new Dimension(500, 500);
public LayeredPaneLayout(final Container target) {
this.target = target;
}
#Override
public void addLayoutComponent(final String name, final Component comp) {
}
#Override
public void layoutContainer(final Container container) {
for (final Component component : container.getComponents()) {
component.setBounds(new Rectangle(0, 0, target.getWidth(), target.getHeight()));
}
}
#Override
public Dimension minimumLayoutSize(final Container parent) {
return preferredLayoutSize(parent);
}
#Override
public Dimension preferredLayoutSize(final Container parent) {
return preferredSize;
}
#Override
public void removeLayoutComponent(final Component comp) {
}
}

The JLayeredPane acts as if it were using null layout. You must specify your JPanel's size and position when adding it to the JLayeredPane. One of the few times I'll recommend this:
JPanel p1 = new JPanel(new BoxLayout(p1, BoxLayout.X_AXIS));
JPanel p2 = new JPanel(new BoxLayout(p2, BoxLayout.Y_AXIS));
JLayeredPane lp = new JLayeredPane();
lp.add(p1, 1);
lp.add(p2, 0);
p1.setSize(lp.getPreferredSize());
p2.setSize(lp.getPreferredSize());

I was having a similar problem. Instead of passing the layer when you add the panes, try to do it separately:
lp.setLayer(p1, 1);
lp.setLayer(p2, 0);
lp.add(p1);
lp.add(p2);
Layers must be defined before adding components.
You can also try to use JLayeredPane.DEFAULT_LAYER instead of 0.
In my case, my JLayeredPane had GridBagLayout, I don't know if it works with default layout.

Related

Java Swing JTabbedPane layout

I am new to Swing and cannot find a page that helps me understand JTabbedPane. I cannot find a way to control the layout of components of the tabbed panels. I can layout each of my panels correctly as separate GUIs but not in a tabbed pane like I need to do. I would like to use the BorderLayout not FlowLayout.
Also, you can see I'm trying to use colors to keep track of my panels and their components. I cannot set the background of the JTabbedPane. It is still the default grey. Can someone tell me why this is?
Thank you for any advice you can give.
What I have so far appears to follow a 'flow layout' despite any changes I've tried
(Methods have been removed or nearly removed to keep code shorter)
public class GUIFrame extends JFrame {
public GUIFrame(String title) {
JFrame frame = new JFrame(title);
Container c = frame.getContentPane();
buildGUI(c);
setFrameAttributes(frame);
}
private void buildGUI(Container c) {
c.setLayout(new BorderLayout());
c.setBackground(Color.BLACK);
JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
tabs.setBackground(Color.YELLOW);
c.add("Center", tabs);
tabs.addTab("Specialty", new SpecialtyPanel());
tabs.addTab("Treatment", new TreatmentPanel());
tabs.addTab("Doctor", new DoctorPanel());
tabs.addTab("Patient", new PatientPanel());
}
private void setFrameAttributes(JFrame f) {
f.setSize(500, 500);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String args[]) {
MedicalSystemIO test = new MedicalSystemIO();
new GUIFrame("Tabbed Title");
}
public class SpecialtyPanel extends JPanel implements ActionListener {
JTextField jteInput = null;
DefaultListModel<String> model = new DefaultListModel<String>();
JList<String> list = new JList(model);
JScrollPane pane = new JScrollPane(list);
public SpecialtyPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createLineBorder(Color.black));
buildGUI(panel);
}
private void buildGUI(JPanel panel) {
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
//panel.setLayout(new BorderLayout());
add("North", jpaInput);
add("Center", jpaProcess);
add("South", jpaOutput);
}
private JPanel createInputPanel() {
JPanel jpaInput = new JPanel();
jpaInput.setBackground(Color.RED);
return jpaInput;
}
private JPanel createProcessPanel() {
JPanel jpaProcess = new JPanel();
jpaProcess.setBackground(Color.BLUE);
return jpaProcess;
}
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
return jpaOutput;
}
The SpecialtyPanel is shown that way (flow layout) as you are putting the components on it in the wrong way:
No need for passing a new panel into the buildGUI method as you want to put them directly on the SpecialtyPanel which already is a JPanel,
you commented out the setting of the BorderLayout and
you used the wrong notation of passing the layout constraints in the add methods.
Your constructor and build method should look like this:
public SpecialtyPanel() {
buildGUI();
}
private void buildGUI() {
setBorder(BorderFactory.createLineBorder(Color.black));
JPanel jpaInput = createInputPanel();
JPanel jpaProcess = createProcessPanel();
JPanel jpaOutput = createOutputPanel();
setLayout(new BorderLayout());
add(jpaInput, BorderLayout.NORTH);
add(jpaProcess, BorderLayout.CENTER);
add(jpaOutput, BorderLayout.SOUTH);
}
To have the panel another color than gray you have to color the component that is put on the tabbed pane as it covers the whole space. Add the desired color to the buildGUI method, e.g.:
private void buildGUI(JPanel panel) {
// ...
setBackground(Color.YELLOW);
}
As a JPanel is opaque by default (that means not transparent), you need to set panels on top (except those which you colored explicitly) to be transparent. In case of SpecialtyPanel:
private JPanel createOutputPanel() {
JPanel jpaOutput = new JPanel();
jpaOutput.add(pane);
jpaOutput.setOpaque(false); // panel transparent
return jpaOutput;
}

How to remove the padding between in the JPanel still using a flow layout?

Here's the portion of my java application GUI that I have a question about.
What this GUI consists is a blue JPanel(container) with default FlowLayout as LayoutManager that contains a Box which contains two JPanels(to remove the horizontal spacing or i could have used setHgaps to zero for that matter instead of a Box) that each contains a JLabel.
Here's my code for creating that part of the GUI.
private void setupSouth() {
final JPanel southPanel = new JPanel();
southPanel.setBackground(Color.BLUE);
final JPanel innerPanel1 = new JPanel();
innerPanel1.setBackground(Color.ORANGE);
innerPanel1.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
innerPanel1.add(new JLabel("Good"));
final JPanel innerPanel2 = new JPanel();
innerPanel2.setBackground(Color.RED);
innerPanel2.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
innerPanel2.add(new JLabel("Luck!"));
final Box southBox = new Box(BoxLayout.LINE_AXIS);
southBox.add(innerPanel1);
southBox.add(innerPanel2);
myFrame.add(southPanel, BorderLayout.SOUTH);
}
My question is how would i get rid the vertical padding between the outer JPanel(the blue one) and the Box?
I know this is padding because i read on Difference between margin and padding? that "padding = space around (inside) the element from text to border."
This wouldn't work because this has to due with gaps(space) between components.- How to remove JPanel padding in MigLayout?
I tried this but it didn't work either. JPanel Padding in Java
You can just set the gaps in the FlowLayout, i.e.
FlowLayout layout = (FlowLayout)southPanel.getLayout();
layout.setVgap(0);
The default FlowLayout has a 5-unit horizontal and vertical gap. Horizontal doesn't matter in this case as the BorderLayout is stretching the panel horizontally.
Or simple initialize the panel with a new FlowLayout. It'll be the same result.
new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
Edit:
"I tried that, didn't work.."
Works for me...
Setting the gap ↑ Not setting the gap ↑
import java.awt.*;
import javax.swing.*;
public class Test {
public void init() {
final JPanel southPanel = new JPanel();
FlowLayout layout = (FlowLayout)southPanel.getLayout();
layout.setVgap(0);
southPanel.setBackground(Color.BLUE);
final JPanel innerPanel1 = new JPanel();
innerPanel1.setBackground(Color.ORANGE);
innerPanel1.add(new JLabel("Good"));
final JPanel innerPanel2 = new JPanel();
innerPanel2.setBackground(Color.RED);
innerPanel2.add(new JLabel("Luck!"));
final Box southBox = new Box(BoxLayout.LINE_AXIS);
southBox.add(innerPanel1);
southBox.add(innerPanel2);
southPanel.add(southBox); // <=== You're also missing this
JFrame myFrame = new JFrame();
JPanel center = new JPanel();
center.setBackground(Color.yellow);
myFrame.add(center);
myFrame.add(southPanel, BorderLayout.SOUTH);
myFrame.setSize(150, 100);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setLocationByPlatform(true);
myFrame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new Test().init();
}
});
}
}
Note: Always post a runnable example (as I have done) for better help. You say it doesn't work, but it always works for me, so how would we know what you're doing wrong without some code that will run and demonstrate the problem?

JScrollPane scrolling issue

Initially all the components are aligning only in the Horizontal direction in a single row.
Then I set the size of scroll enabled panel as below
main.setPreferredSize(scroll.getViewport().getSize());
main.setMaximumSize(new Dimension(scroll.getViewport().getWidth(),Integer.MAX_VALUE));
And it worked the components start aligning in multiple lines,and not going beyond the screen horizontally. But why the vertical scroll is not happening, In fact the components are getting overridden, the height is also getting fixed as of viewport size even the maxsize is defined.
Please help.........
What i need is only vertical scrolling..... The components should not go beyond screen horizontally, but can go beyond screen vertically.
UPDATE with the code: Now all I want is the buttons should not go beyond the screen horizontally instead can use vertical scroll if the whole window is occupied.
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setTitle("JAVA GUI");
JPanel main = new JPanel();
JScrollPane scroll = new JScrollPane(main);
BoxLayout box = new BoxLayout(main, BoxLayout.Y_AXIS);
main.setLayout(box);
main.add(new JLabel("row 1"));
JPanel panel1 = new JPanel(new FlowLayout());
for(int i=0;i<200;i++){
panel1.add(new JButton("b"+i));
}
JPanel panel2 = new JPanel(new FlowLayout());
for(int i=0;i<200;i++){
panel2.add(new JButton("b"+i));
}
JPanel panel3 = new JPanel(new FlowLayout());
for(int i=0;i<200;i++){
panel3.add(new JButton("b"+i));
}
Border border = BorderFactory.createLineBorder(Color.black,10);
panel1.setBorder(border);
panel2.setBorder(border);
panel3.setBorder(border);
main.add(panel1);
main.add(new JLabel("row2"));
main.add(panel2);
main.add(new JLabel("row3"));
main.add(panel3);
frame.setContentPane(scroll);
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.setVisible(true);
}
}
Scrollbars only appear when the preferred size of the component is greater than the size of the scrollpane.
A FlowLayout will wrap components, but it will not change the preferred size.
Check out the Wrap Layout which extends FlowLayout and will recalculate the preferred size for you when components wrap.
As camickr pointed out in https://stackoverflow.com/a/21816061 , the main problem here is that the FlowLayout does not properly compute the preferred size of the component. The WrapLayout that he linked solves this issue basically. But in this case, this is still not sufficient, because the preferred size of the panels will simply be "as wide as necessary to show all buttons". In order to really wrap the buttons, you also have to replace your main panel with a panel that implements the Scrollable interface and returns true in Scrollable#getScrollableTracksViewportWidth()
class ScrollablePanel extends JPanel implements Scrollable
{
#Override
public Dimension getPreferredScrollableViewportSize()
{
return getPreferredSize();
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction)
{
return 1;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
return 1;
}
#Override
public boolean getScrollableTracksViewportWidth()
{
return true;
}
#Override
public boolean getScrollableTracksViewportHeight()
{
return false;
}
}
So in your code:
JPanel main = new ScrollablePanel(); // Use ScrollablePanel here
JScrollPane scroll = new JScrollPane(main);
...
JPanel panel1 = new JPanel(new WrapLayout()); // Use WrapLayout here
for(int i=0;i<200;i++){
panel1.add(new JButton("b"+i));
}

java swing how to restrict JScrollPane to only vertical

Check out if you can help on this.
I need to restrict the scrolling to only vertical when using JScrollPane.
REMEMBER: not disabling the hortizontal scroll bar by using HORIZONTAL_SCROLLBAR_NEVER, which just disable horizontal. And i need is the components should not go beyond the window horizontally.
Add this to the container within the JScrollPane:
#Override
public java.awt.Dimension getPreferredSize() {
int h = super.getPreferredSize().height;
int w = getParent().getSize().width;
return new java.awt.Dimension(w, h);
}
At first I suggest you to check this out.
http://www.java-tips.org/java-se-tips/javax.swing/how-to-use-a-scrollbar-in-both-vertical-and-horizontal-dire.html
and you can actually try these lines of codes as well:
public class AddScroll
{
public static void main(String[] args)
{
JPanel panel = new JPanel();
JScrollPane scrollBar = new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JFrame frame = new JFrame("AddScrollBarToJFrame");
frame.add(scrollBar);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setVisible(true);
}
}

Overlay panel (above another)

I am learning how to use Swing and found myself quite difficult task.
What I am trying to accomplish: I want to have panel (call it menu panel) on the left side (let's say 100px width) and the second panel (call it content panel), which takes the rest of available place.
In menu panel there are 3 buttons. When I press on of them, to the right side of menu panel (over content panel) second menu panel (submenu) should appear (and it should start in the middle of button which was pressed).
It may be hard to understand, so I've created simple draft:
I tried JLayeredPane but there were problems with resizing window (elements in Layered Pane didn't resize).
JLayeredPane miss implementations for LayoutManager, you have to setPreferredSize or setBounds manually for sizing/place JComponents,
there is one possible workaround you can add ComponentListener to the JFrame, then on componentResized(ComponentEvent e) you can resize/replace JComponent(s) to the desired Bounds
for example
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class LayeredPaneWithOverlap {
private JTextArea textArea = new JTextArea(2, 10);
private JPanel textPanel = new JPanel(new BorderLayout());
private JTable table = new JTable(30, 5);
private JScrollPane scroll = new JScrollPane(table);
private JLayeredPane layer = new JLayeredPane();
private JFrame frame = new JFrame("Frame with resiziable JLayeredPane");
public void makeUI() {
textArea.setBorder(new LineBorder(Color.DARK_GRAY));
textArea.setText("Frame with resiziable JLayeredPane");
textPanel.setOpaque(false);
textPanel.add(textArea, BorderLayout.NORTH);
Font font = textArea.getFont();
FontMetrics fontMetrics = textArea.getFontMetrics(font);
int h = fontMetrics.getHeight() + frame.getInsets().top +
textPanel.getInsets().top + textArea.getInsets().top
+ textArea.getInsets().bottom;
scroll.setBounds(0, h, 400, 300);
layer.add(textPanel, new Integer(2));
layer.add(scroll, new Integer(1));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 400);
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
resizeAll();
}
});
}
});
frame.setLocationRelativeTo(null);
frame.add(layer);
resizeAll();
frame.setVisible(true);
}
void resizeAll() {
Insets insets = frame.getInsets();
int w = frame.getWidth() - insets.left - insets.right;
int h = frame.getHeight() - insets.top - insets.bottom;
textPanel.setSize(w, h);
scroll.setSize(w, h - scroll.getY());
layer.revalidate();
layer.repaint();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LayeredPaneWithOverlap().makeUI();
}
});
}
}
You can set a layoutmanager for the layered pane, javax.swing.OverlayLayout uses the full available space and allows resizing.
JLayeredPane layer = new JLayeredPane();
layer.setLayout(new OverlayLayout(layer));
You probably don't want the submenu to occupy the fullspace. To avoid it you can override its get…size-methods. Or you can add a second LayeredPane (for it's transperancy and it's layoutmanager), set a normal BoxLayout and use a spacer.
JPanel normalContents = new JPanel();
layer.add(normalContents, JLayeredPane.DEFAULT_LAYER);
JLayeredPane subMenuAuxiliaryLayer = new JLayeredPane()
subMenuAuxiliaryLayer.setLayout(new BoxLayout(subMenuAuxiliaryLayer, BoxLayout.LINE_AXIS));
layer.add(subMenuAuxiliaryLayer, JLayeredPane.PALETTE_LAYER);
JPanel submenuContents = new JPanel();
subMenuAuliliaryLayer.add(submenuContents);
subMenuAuxiliaryLayer.add(Box.createHorizontalGlue());
contentPanel.setLayout(null); // Absolute positioning of children.
#Override
public void actionPerformed(ActionEvent evt) {
final JButton btn = (JButton) evt.getSource();
final int buttonY = btn.getY(); // Must be final for usage in new Runnable object.
SwingUtilities.invokeLater(new Runnable() { // Return fast from event handling.
#Override
public void run() {
JPanel child = new JPanel();
child.setBackground(Color.RED); // So we'll see it.
child.setBounds(0, buttonY, 100, 300);
contentPanel.removeAll(); // Clear content panel of prior additions.
contentPanel.add(child); // Add a new panel.
contentPanel.repaint(10L);
}
});
}
The JLayeredPane works by defualt with no Layout manager, which means that you are using absolute positioning and no resizing. You could add a resize listener and adjust positions and size of inner components from code, as you see fit.
If you don't want to do this from code, you will need a layout manager, nothing fancy, just something to fill the container as it resizes. But here's the thing... if you add a layout manager, it will layout the components as if they are all in one layer, but most layout managers don't overlap their children so they are useless. The only one you could use is the OverlayLayout - it can also resize children. But using an OverlayLayout with JLayeredPane is overkill. You can just use OverlayLayout with a JPanel. So, yes, JLayeredPane is kind of useless. I recommend using a JPanel with an OverlayLayout instead.
Here is how to set things up so that you can have great control over almost any overlapping UI scenario out there: Using a JPanel with an OverlayLayout, have a separate transparent JPanel for each layer. In this way you can combine various LayoutManagers on different layers, by setting a diferent layout manager for each pane, including absolute positioning if necessary. Then add your visible components inside the panels representing the layers. Don't add them directly to the OverlayLayout panel. Just make sure that all of the JPanels you are using as layers have setAlignmentX and Y to center (0.5f) so that they fill the entire OverlayLayout panel as it resizes.

Categories

Resources