Can't get JPanel to size properly in Swing - java

I'm just learning Swing, and built a sample GUI for myself as practice. Very basic, it has three panels on the West, Center, and East sides of the JFrame. In the last panel, there is another panel and a button. The panel is my extension of JPanel, DrawPanel. It draws a random Rectangle every time the button beneath it Repaint is clicked. This is my code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Tester
{
public static DrawPanel panelEastDrawPanelCenter;
public static void main()
{
Tester gui = new Tester();
gui.go();
}
public void go()
{
JFrame frame = new JFrame("This is the title");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create all the Western Panel components
JPanel panelWest = new JPanel();
// panelWest.setLayout(new BorderLayout());
JButton panelWestButtonWest = new JButton("Western-most West Button");
JButton panelWestButtonCenter = new JButton("Western-most Center Button");
JButton panelWestButtonEast = new JButton("Western-most East Button");
// Create all Center Panel components
JPanel panelCenter = new JPanel();
panelCenter.setBackground(Color.darkGray);
JButton panelCenterButtonCenter = new JButton("Center Button");
// Create all East Panel components
JPanel panelEast = new JPanel();
panelEastDrawPanelCenter = new DrawPanel();
panelEastDrawPanelCenter.setSize(50,50);
JButton panelEastButtonSouth = new JButton("Repaint");
panelEastButtonSouth.addActionListener(new panelEastButtonSouthListener());
// Add everything to the GUI
// West Panel
frame.getContentPane().add(BorderLayout.WEST, panelWest);
panelWest.add(BorderLayout.WEST, panelWestButtonWest);
panelWest.add(BorderLayout.CENTER, panelWestButtonCenter);
panelWest.add(BorderLayout.EAST, panelWestButtonEast);
// Center Panel
frame.getContentPane().add(BorderLayout.CENTER, panelCenter);
panelCenter.add(BorderLayout.CENTER, panelCenterButtonCenter);
// East Panel
frame.getContentPane().add(BorderLayout.EAST, panelEast);
panelEast.add(panelEastDrawPanelCenter);
panelEast.add(panelEastButtonSouth);
frame.pack();
//frame.setSize(frame.getWidth(), 500);
frame.setVisible(true);
}
class panelEastButtonSouthListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
panelEastDrawPanelCenter.repaint();
}
}
class DrawPanel extends JPanel // JPanel that displays a rectangle upon clicking the button "Repaint"
{
public void paintComponent(Graphics g)
{
g.setColor(Color.WHITE); // Removes previous rectangle
g.fillRect(0,0, this.getWidth(),this.getHeight());
g.setColor(randColor()); // Puts a new rectangle on screen, rand size and color
int height = (int)(Math.random() * this.getHeight());
int width = (int)(Math.random() * this.getHeight());
int x = (int)(Math.random() * 20);
int y = (int)(Math.random() * 20);
g.fillRect(x,y, height,width);
}
public Color randColor()
{
int r = (int)(Math.random() * 255);
int g = (int)(Math.random() * 255);
int b = (int)(Math.random() * 255);
return new Color(r, g, b);
}
}
}
The problem I am encountering is that although I explicitly setSize() for the DrawPanel object (panelEastDrawPanelCenter) to 50×50, when I run the program it (DrawPanel) is still a small panel next to the button, and panelEast (the container for DrawPanel and the button) is still the same width (and will never get wider). I realize that I can say
frame.pack();
frame.setSize(frame.getWidth(), 500);
if I set the east panel to use a layout of either BorderLayout or BoxLayout, and this will make the DrawPanel display bigger.
But I do not understand why setting the size of the DrawPanel object does not actually change its size, and why it remains small regardless of me doing setSize(50,50);
How would I get the middle panel to stop resizing so huge, so that the East panel can resize to be wider? Or how do I resize the East panel?

The BorderLayout respects the preferred width of its west and east components. And it respects the preferred width and height of its center component. So, your DrawPanel should override getPreferredSize() and return the appropriate preferred size.
Since it's placed at the center of a BorderLayout which also has a south component, the preferred width of the east panel will be the max of the preferred width of the draw panel and of the button.

Have you tried using panel.setPerferredSize(Dimension size);

Try the setPreferredSize(Dimension) method instead of setSize(int, int) and call a pack() after that.

Related

ImageIcon extends beyond window, resizing window expands all of components instead of showing more

private void setupGUI(){
// Setup Frame
f = new JFrame("Shape Image Generator");
f.setBounds(500, 150, 450, 350);
f.setLayout(new GridLayout(8,1));
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent windowEvent){
System.exit(0);
}
});
}
I create the frame above, then 8 panels. I create various components and add them to the panels and everything works fine. Until I created an ImageIcon and added it to a label and added that label to the 8th panel. The image used is 140x129 pixels. The problem is, only the top.... maybe 1/4 of the image is showing. If I change the frames dimensions in the code, more empty space is created between each panel, but only a slight bit more of the image is shown, so the image is still off of the screen. I'd say the window is easily adding 10 pixels of spacing for every 1 more pixel of the image it shows. If I drag the corners of the window to expand it, the same thing happens. If the window is maximized I still can only see a little over half of my now very stretched image.
Things I tried:
None of my components have preferred dimensions set, but I tried setting a preferred dimension for the label then panel that contains the ImageIcon and it only added the difference between the image and preferred size in gray space above the image, pushing it further offscreen. So, I undid that.
Adding the label containing the ImageIcon to a different panel which was not the 8th and last panel, in this case, the image is still cut off, but at the point that it gets cut off, the components on the panel underneath it appear (over top of the background coloring which cuts off the image).
Exhaustively Googling this situation with about 30 different ways of phrasing it and not finding a solution.
(row1 - row8 are JPanels, I didn't include the coding for them)
ImageIcon iconStart = createImageIcon("/images/ShapeClipart.png", "Shapes");
JLabel imgLabel = new JLabel();
row8.add(imgLabel);
// Add image to image label
imgLabel.setIcon(iconStart);
// Add panels to frame
f.add(row1);
f.add(row2);
f.add(row3);
f.add(row4);
f.add(row5);
f.add(row6);
f.add(row7);
f.add(row8);
f.setVisible(true);
Window at execution
Window when stretched
edit:
adding f.pack() makes a very tall skinny window (the windows height taller than my screen) but it still looks like when I manually expand the window (empty space between panels, image partially offscreen), even if I take out f.setBounds and only use f.setLocation.
You are using a GridLayout. This gives all of the enclosed panels the same amount of space. In this case it is a vertical grid.
You should probably use something a bit different. I might try a BorderLayout in the JFrame and put the a panel containing the top seven panels (in a GridLayout) into the CENTER, and then put the JLabel into the SOUTH portion of the JFrame.
There are other ways to lay it out, but this is the first I could think of.
GridLayout makes each cell in the grid the same size and the size of each cell is determined by the largest Component contained in the grid.
In your code, the icon is the largest component and you also have only one column in your grid so every row has the same height as your icon.
Since you also limit the size of your JFrame by calling method setBounds(), the Swing infrastructure cuts off the icon so that all the components fit into the bounds you specified.
One alternative, but not the only one, is to use BoxLayout since it uses the preferred size of each of its contained components.
Here is a sample GUI that matches the screen capture that you posted and uses BoxLayout.
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.net.URL;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
public class Shapes23 implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showGui();
}
private JPanel createEighthRow() {
JPanel eighthRow = new JPanel();
URL url = getClass().getResource("paint-bursht.jpg");
Icon ico = new ImageIcon(url);
JLabel label = new JLabel(ico);
eighthRow.add(label);
return eighthRow;
}
private JPanel createFifthRow() {
JPanel fifthRow = new JPanel();
JTextField textField = new JTextField(20);
fifthRow.add(textField);
return fifthRow;
}
private JPanel createFirstRow() {
JPanel firstRow = new JPanel();
JLabel label = new JLabel("2D Shapes");
firstRow.add(label);
return firstRow;
}
private JPanel createFourthRow() {
JPanel fourthRow = new JPanel();
fourthRow.add(createRadioButton("Sphere"));
fourthRow.add(createRadioButton("Cube"));
fourthRow.add(createRadioButton("Cone"));
fourthRow.add(createRadioButton("Cylinder"));
fourthRow.add(createRadioButton("Torus"));
return fourthRow;
}
private JPanel createMainPanel() {
JPanel mainPanel = new JPanel();
BoxLayout layout = new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS);
mainPanel.setLayout(layout);
mainPanel.add(createFirstRow());
mainPanel.add(createSecondRow());
mainPanel.add(createThirdRow());
mainPanel.add(createFourthRow());
mainPanel.add(createFifthRow());
mainPanel.add(createSixthRow());
mainPanel.add(createSeventhRow());
mainPanel.add(createEighthRow());
return mainPanel;
}
private JRadioButton createRadioButton(String text) {
JRadioButton radioButton = new JRadioButton(text);
return radioButton;
}
private JPanel createSecondRow() {
JPanel secondRow = new JPanel();
secondRow.add(createRadioButton("Circle"));
secondRow.add(createRadioButton("Rectangle"));
secondRow.add(createRadioButton("Square"));
secondRow.add(createRadioButton("Triangle"));
return secondRow;
}
private JPanel createSeventhRow() {
JPanel seventhRow = new JPanel();
JButton button = new JButton("Enter");
seventhRow.add(button);
return seventhRow;
}
private JPanel createSixthRow() {
JPanel sixthRow = new JPanel();
JTextField textField = new JTextField(20);
sixthRow.add(textField);
return sixthRow;
}
private JPanel createThirdRow() {
JPanel thirdRow = new JPanel();
JLabel label = new JLabel("3D Shapes");
thirdRow.add(label);
return thirdRow;
}
private void showGui() {
frame = new JFrame("Shape Image Generator");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Shapes23());
}
}
Here is a screen capture of how it looks. Note that I couldn't find the same icon as in your screen capture so I just used a different one.

Java Swing GUI with text and picture pane [duplicate]

This is for Tetris. The glass (blue) is left, and the controls (red panel) are situated in the right. In other words, now I would like just to have a frame divided into two parts: left (wider) part is blue, right part is red. Nothing more. But I seem to fail to do this.
So, my logic is: let the frame have FlowLayout. Then I add two panels which means that they are expected to be put in a row.
I prepared this:
public class GlassView extends JFrame{
public GlassView(){
this.setSize(600, 750);
this.setVisible(true);
this.setLayout(new FlowLayout());
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel glass = new JPanel();
glass.setLayout(new BoxLayout(glass, BoxLayout.Y_AXIS));
glass.setSize(450, 750);
glass.setBackground(Color.BLUE);
glass.setVisible(true);
this.add(glass);
JPanel controls = new JPanel();
controls.setLayout(new BoxLayout(controls, BoxLayout.Y_AXIS));
controls.setSize(150, 750);
controls.setBackground(Color.RED);
controls.setVisible(true);
this.add(controls);
}
}
But only a gray frame is visible on the screen. Could you help me understand why?
As Amir said you want to use a JSplitPane for this. I have added this in your code. Have a look at this.
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
GlassView view = new GlassView();
}
private static class GlassView extends JFrame {
private int width = 600;
private int height = 750;
public GlassView() {
this.setSize(width, height);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel glass = new JPanel();
glass.setSize(450, 750);
glass.setBackground(Color.BLUE);
glass.setVisible(true);
JPanel controls = new JPanel();
controls.setSize(150, 750);
controls.setBackground(Color.RED);
controls.setVisible(true);
JSplitPane splitPane = new JSplitPane();
splitPane.setSize(width, height);
splitPane.setDividerSize(0);
splitPane.setDividerLocation(150);
splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setLeftComponent(controls);
splitPane.setRightComponent(glass);
this.add(splitPane);
}
}
How to divide a frame into two parts
...
I would like just to have a frame divided into two parts: left (wider) part is blue, right part is red.
You want to use is a SplitPane.

Multiple JPanel on JFrame [duplicate]

Below is an example of adding two panels to a frame. Only one panel (the 2nd, red panel) appears.
Why does the first panel disappear?
import java.awt.Color;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame() {
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(getColoredPanel(Color.GREEN));
f.add(getColoredPanel(Color.RED));
f.pack();
f.setVisible(true);
}
private JPanel getColoredPanel(Color color) {
JPanel p = new JPanel();
p.setBackground(color);
p.setBorder(new EmptyBorder(20, 150, 20, 150));
return p;
}
public static void main(String[] args) {
Runnable r = DisappearingPanelInFrame::new;
SwingUtilities.invokeLater(r);
}
}
The default layout of a JFrame (or more specifically in this case, the content pane of the frame) is a BorderLayout.
When adding a component to a BordeLayout with no constraint, the Swing API will put the component in the CENTER.
A BorderLayout can contain exactly one component in each of the 5 layout constraints.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
As to what would be a better approach depends on the specific needs of the user interface.
When a second component is added to the same (in this case CENTER) constraint of a BorderLayout, this implementation of Java will display the last component added.
Not strictly true.
The BorderLayout will only reset the bounds (ie size and location) of the last component added to a specific constraint location. This is different from other layout managers in that they will reset the bounds of all components in the container.
In the example code the red panel was the "active" panel at the time the frame was validated by using the pack() method and therefore only its bound were set and therefore only it was painted.
For a demonstration of this process run the example below using the following steps:
Click on the "Add Panel in Center" button, nothing appears to happen even though the blue panel was added to the center.
Move the mouse over the red panel and the buttons will appear because the mouse rollover logic will cause the buttons to be repainted.
Now increase the frame width and the blue panel will appear under the red panel.
The code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class DisappearingPanelInFrame {
DisappearingPanelInFrame()
{
JButton button = new JButton ("Add Panel In Center");
button.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
JPanel blue = new JPanel();
blue.setBackground( Color.BLUE );
blue.add( new JButton("Button 1") );
blue.add( new JButton("Button 2") );
Component c = (Component)e.getSource();
Window window = SwingUtilities.windowForComponent(c);
window.add(blue );
window.revalidate();
window.repaint();
}
});
JFrame f = new JFrame(this.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.add(new ColoredPanel(Color.GREEN));
//f.pack();
f.add(new ColoredPanel(Color.RED));
f.add(button, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new DisappearingPanelInFrame();
}
};
SwingUtilities.invokeLater(r);
}
}
class ColoredPanel extends JPanel {
ColoredPanel(Color color) {
setBackground(color);
setBorder(new EmptyBorder(20, 150, 20, 150));
}
}
When the blue panel is added to the BorderLayout and when the revalidate() is invoked the bounds of the blue panel are set.
However, because of the way Swing does ZOrder painting the blue panel is painted first and then the red panel is painted on top of the blue panel. The green panel still has a size of (0, 0) since it was never the "active" panel in the BorderLayout.CENTER when the frame was initially validated with the pack() method.
When the frame is resized, the blue panel being the "active" panel in the BorderLayout.CENTER, has its bounds adjusted, so it will now fill the extra space in the frame.
Now for another test:
pack() the frame after adding the green panel to the frame.
run the code and increase the width of the frame and the red and green frame will appear
then click the button and increase the width and now all 3 panels will appear
Bottom line is still the same:
Don't try to add multiple panels to the same constraint of a BorderLayout. If you do, then make sure you remove the previous panel or you have the potential for unexpected results.

Java Swing - Attempting to get a line to draw and stay with my sliders as I move them

I am a newb and am trying to get a line to draw with my xslider and yslider so it would create cross hairs on the canvas panel. I cannot figure this out. The idea is that when I push the "Show" button a circle is to appear centered on the crosshairs set by the sliders. I used internalframes to create the button location and canvas for the circle and sliders. I need lines connected to the sliders. I cannot change the coding as to how the sliders work in tandem, part of expectations. Please assist.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class CircleViewer2 extends JPanel
{
//Variables
Ellipse2D.Double circle;
static Color FillColor = Color.blue;
static String ShowHideName = null;
static JSlider xSlider;
static JSlider xSlider2;
static JSlider ySlider;
static JSlider ySlider2;
//Creation of the circle utilizing Ellipse2D
public CircleViewer2(int radius)
{
circle = new Ellipse2D.Double(0, 0, radius, radius);
setOpaque(false);
}
//Setting PreferredSize
public Dimension getPreferredSize()
{
Rectangle bounds = circle.getBounds();
return new Dimension(bounds.width, bounds.height);
}
//Establishing parameters for Drawing the Circle Via Paint
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(FillColor);
g2.fill(circle);
}
public static void main(String[] args)
{
final JPanel center = new JPanel();
center.setLayout(null);
center.setPreferredSize(new Dimension(400,400));
ShowHideName = "Show";
final JButton ShowHideButton = new JButton(ShowHideName);
ShowHideButton.setPreferredSize(new Dimension(75, 25));
ShowHideButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (ShowHideName.equals("Show"))
{
int xCoord = xSlider.getValue();
System.out.println(xCoord);
int yCoord = ySlider.getValue();
System.out.println(yCoord);
CircleViewer2 component = new CircleViewer2(50);
component.setLocation(xCoord,yCoord);
component.setSize(component.getPreferredSize());
center.add(component);
ShowHideName = "Hide";
center.repaint();
}
else
{
ShowHideName = "Show";
center.removeAll();
center.updateUI();
}
ShowHideButton.setText(ShowHideName);
}
});
final JButton ColorButton = new JButton("Color");
ColorButton.setPreferredSize(new Dimension(75, 25));
ColorButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
FillColor = JColorChooser.showDialog(null, "Pick a Color", Color.blue);
}
});
JFrame frame = new JFrame();
JInternalFrame canvas = new JInternalFrame();
JInternalFrame buttonFrame = new JInternalFrame();
JPanel buttonPanel = new JPanel();
buttonPanel.add(ShowHideButton);
buttonPanel.add(ColorButton);
javax.swing.plaf.InternalFrameUI ifu= buttonFrame.getUI();
((javax.swing.plaf.basic.BasicInternalFrameUI)ifu).setNorthPane(null);
buttonFrame.setBounds(0, 500, 500, 200);
buttonFrame.add(buttonPanel, BorderLayout.CENTER);
buttonFrame.setVisible(true);
xSlider = new JSlider(SwingConstants.HORIZONTAL,0,380,10);
BoundedRangeModel xmodel = xSlider.getModel();
xSlider2 = new JSlider(SwingConstants.HORIZONTAL);
xSlider2.setModel(xmodel);
ySlider = new JSlider(SwingConstants.VERTICAL,0,350,10);
BoundedRangeModel ymodel = ySlider.getModel();
ySlider.setInverted(true);
ySlider2 = new JSlider(SwingConstants.VERTICAL);
ySlider2.setModel(ymodel);
ySlider2.setInverted(true);
canvas.add(center, BorderLayout.CENTER);
canvas.add(xSlider, BorderLayout.SOUTH);
canvas.add(xSlider2, BorderLayout.NORTH);
canvas.add(ySlider, BorderLayout.EAST);
canvas.add(ySlider2, BorderLayout.WEST);
canvas.setBounds(0, 0, 500, 550);
canvas.setVisible(true);
javax.swing.plaf.InternalFrameUI ifu2 = canvas.getUI();
((javax.swing.plaf.basic.BasicInternalFrameUI)ifu2).setNorthPane(null);
frame.add(canvas, BorderLayout.NORTH);
frame.add(buttonFrame, BorderLayout.SOUTH);
frame.setBounds(0, 0, 500, 530);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
This is not a good implementation of the problem. You can inherit the JFrame and make the code clearer, it's the common method to play with Swing components. Any way, Add a change Listener to your sliders and change the location of the Center Panel according to the Sliders Value.
Something like that:
xSlider.addChangeListener( e -> {
center.setLocation(new Point(xSlider.getValue(), (int) center.getLocation().getY());
});
and so on.
This is a good place to start with Java/Swing GUI best practices
Your code looks way more complex than it needs to be, and I would try to simplify it greatly. Some suggestions:
Get rid of all the JInternalFrames and use JPanels instead.
Create a JPanel, say called drawingPanel, that has its paintComponent(Graphics g) overridden and perhaps its getPreferredSize() overridden, place this JPanel BorderLayout.CENTER in your main GUI.
In the paintComponent method, have the logic to draw the circles and the crosshairs based on fields of your class.
When a JSlider moves, have its ChangeListener change the state of the corresponding field, and then have it call repaint() on the drawing panel so that it will draw the changes.
When the show button is pressed, have it change the state of a boolean variable and then call repaint().
Have the drawingPanel use the boolean in its paintComponent method to decide whether or not to draw the filled circle.
Have the drawingPanel draw the lines in its paintComponent method based on the value returned by the JSliders. Again you're calling repaint in the JSlider's listeners, so the lines will move.
Do not add and remove components on button clicks since this adds unnecessary complexity which makes things much harder to code.
Don't move the center component or any component. Just use the fixed drawingPanel JPanel and move the location of the circle and the lines that it draws.
Since this is homework, I'm going to avoid posting code as the coding should be up to you. Much luck!

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