Overlay panel (above another) - java

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.

Related

Add JPanels dynamically to either a JTable or a JScrollPane

Currently working on a project and I need to add a panel I've made to a scrollpane or a table dynamically. The scrollpane should start out empty and add the panels.
The GuiConstructor is where i make the window.
My problem is that if I don't comment out the setSize in the GuiConstructor, the window starts out very small.
Secondly, when i press the add button, it doesn't add the panels.
public GuiConstructor(){
super(APPLICATION_NAME);
setLayout(new BorderLayout());
LoopControlWindow loopwin = new LoopControlWindow(connect);
add(loopwin , BorderLayout.NORTH);
pack();
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
//this.setSize(500, 500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public class LoopControlWindow extends JPanel {
IConnector connect;
public LoopControlWindow(IConnector connect) {
super(new BorderLayout());
this.connect = connect;
initPane();
}
private void initPane() {
JPanel panel = new JPanel(new GridLayout(3,1));
FolderSearchComp fsc = new FolderSearchComp(connect);
JScrollPane scrollPane = new JScrollPane();
JButton button = new JButton("Add");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panel.add(new FolderSearchComp(connect));
scrollPane.getViewport().setView(panel);
}
});
scrollPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportBorder(new LineBorder(Color.BLACK));
add(scrollPane, BorderLayout.CENTER);
add(button, BorderLayout.SOUTH);
}
This is typical of this style of GUI app. You need to tell the layout manager how big to make the Window initialy without using setSize(). The way to do this is to override getPreferredSize() to return a default size. In your case:
public LoopControlWindow extends JPanel {
private Dimension size;
public LoopControlWindow() {
Preferences prefs = Preferences.userNodeForPackge("your.java.package");
size = new Dimension(prefs.getInt("width", 800), prefs.getInt("height", 600));
}
public Dimension getPreferredSize() {
return size;
}
}
By doing it this way you can store the user preferences for the window dimensions but also provide sensible defaults to start.
You should also make sure that this JPanel is your main panel and is added to the JFrame at BorderLayout.CENTER to ensure that your window gets drawn properly. All other panels should be somewhere inside this one.
Once you have this set up calling pack() will work correctly.
For your first problem, you need to specify a size for the initial JFrame(). One way is to call setSize as you are doing. Another is to override getPreferredSize() to return the default size. And one other option is to find the size of the user's monitor and set the JFrame to be a percentage of that size. That way you can ensure your window always fits on your user's screen.
int height = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().
getBounds().height;
height = (int) (height * .85);
int width = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().
getBounds().width;
width = (int) (width * .85);
frame.setSize(width, height);
Second, you need to call revalidate() and repaint() anytime you add or remove from a layout in order to see the changes.
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
panel.add(new FolderSearchComp(connect));
scrollPane.getViewport().setView(panel);
revalidate();
repaint();
}
});
One note on border layout. The components in it will not resize with your JFrame. Whatever component that is placed in BorderLayout.CENTER will, however. That component will grow to fill all extra space as the JFrame grows. It will also be the component that shrinks when the JFrame windows gets smaller.

How can I remove border on JLabel?

So I am trying to start a graphics program, where I have a JFrame that holds multiple JPanels. The JPanels need to combine to create 1 image, however when I run my program I see borders around the images. I cannot quite distinguish if the border is caused by the JLabel that holds the image or if it is because of the JPanel or because of the Layout Manager.
How can I remove the border? Would i need to change the layout manager? If so how?
import java.util.*;
import java.awt.*;
import javax.swing.*;
public class StarryNight {
JFrame backGround;
JPanel rootPanel;
JLabel rootImage;
public StarryNight(){
backGround = new JFrame("Starry Starry Night");
backGround.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
backGround.setResizable(false);
backGround.setSize(1000,667);
backGround.getContentPane().setBackground(Color.BLACK);
backGround.setLayout(new BoxLayout(backGround.getContentPane(),BoxLayout.Y_AXIS));
rootPanel = new JPanel();
rootPanel.setSize(1000, 667);
rootPanel.setBackground(Color.BLUE);;
rootImage = new JLabel();
rootImage.setIcon(new ImageIcon(getClass().getResource("Starry Night.jpg")));
rootPanel.add(rootImage);
JPanel jap = new JPanel();
jap.setSize(1000,100);
jap.setBackground(Color.GREEN);
backGround.add(rootPanel);
backGround.add(jap);
backGround.pack();
backGround.setVisible(true);
}
private static void runGUI() {
JFrame.setDefaultLookAndFeelDecorated(true);
StarryNight ssn= new StarryNight();
}
public static void main(String args[]){
javax.swing.SwingUtilities.invokeLater(new Runnable(){
public void run(){
runGUI();
}
});
}
}
rootPanel = new JPanel();
By default a JPanel uses a FlowLayout which defaults to allows 5 pixels before/after components. So when you add our image to the panel you will see 5 pixels of space on all sides.
If you don't want that space then look at the FlowLayout API and create a FlowLayout without spaces between the components and then add that layout to the rootPanel. Something like:
rootPanel = new JPanel( new FlowLayout(...) );

setMinimumSize() not working for JButton

The following code describes a button that is instantiated in a JPanel with a BoxLayout in Page Axis:
private class AddInputSetButton extends JButton {
public AddInputSetButton() {
super("+");
setMinimumSize(new Dimension(100, 100));
pack();
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
addInputGroup();
}
});
}
}
I have tried setSize(), setPreferredSize(), and setMinimumSize() to no avail, none of them resize the button. I am still relatively new to Java GUIs, so hopefully it is something simple.
How do I adjust the size of the button?
EDIT: After further inspection, setPreferredSize() changes the size of the JPanel containing the buttons to the right size, but the buttons remain the same size.
JButtons (and a few other components) can be a bit goofy in layout managers. The layout manager is noticing that your button has a preferred size that needs to be respected, so it's adjusting your pane to accommodate. But your JButton is happy doing it's thing (what it thinks is right) unless you really force it to consider the size it's supposed to be.
If you're manually sizing your button (which isn't necessarily recommended), I'd say you should set all three properties (Minimum, maximum, and preferred). Maximum is the key - it forces the button to consider the other two sizes.
Here's a simple example that should work.
import java.awt.Dimension;
import javax.swing.*;
public class ButtonSizes {
private static class AddInputSetButton extends JButton {
Dimension d;
public AddInputSetButton(int width, int height) {
super("+");
d = new Dimension(width, height);
setMinimumSize(d);
setMaximumSize(d);
setPreferredSize(d);
}
}
public static void main(String[] args) {
Box buttons = Box.createVerticalBox();
buttons.add(new AddInputSetButton(100,100));
buttons.add(new AddInputSetButton(200,200));
buttons.add(new AddInputSetButton(300,300));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(buttons);
frame.pack();
frame.setVisible(true);
}
}

adding ScrollBar to JTextArea

I want to add a scroll bar into my text area and I know the simple code for adding scroll bar but when I put the code for scroll bar the whole text area disappears!
What is the problem?
Here is my code:
private JFrame frame;
private JTextArea textarea;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SmsForm window = new SmsForm();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SmsForm() {
initialize();
}
private void initialize() {
frame = new JFrame("???");
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JPanel groupBoxEncryption = new JPanel();
final JTextArea textarea=new JTextArea();
textarea.setBounds(50, 100, 300, 100);
frame.getContentPane().add(textarea);
textarea.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scrollPanePlain = new JScrollPane(textarea);
groupBoxEncryption.add(scrollPanePlain);
scrollPanePlain.setBounds(100, 30, 250, 100);
scrollPanePlain.setVisible(true);
There are a number of issues
You need to add the JPanel groupBoxEncryption to the application JFrame
Don't add the textarea to the frame - components can only have one parent component
As already mentioned, you're using null layout which doesnt size components - forget about not a layout manager.
As JPanel uses FlowLayout by default, you need to override getPreferredSize for the panel groupBoxEncryption. Better yet use a layout manager such as GridLayout that automatically sizes the component
Example
JPanel groupBoxEncryption = new JPanel(new GridLayout());
Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. As such they are not conducive to exact placement of components. To organize the components for a robust GUI, instead use layout managers, or combinations of them, along with layout padding & borders for white space.
Suggest a preferred size for the text area in the number of rows and columns.
Add the text area to a scroll pane before then adding the scroll pane to the GUI.

Layout for displaying panels dynamically with scroll bar

In java, I have been trying to create a panel that can accept other panels with a scroll bar.
I tried using gridlayout, and this works fine, except for the fact that if I only add a few panels, it grows those panels to fit the size of the parent panel.
I tried using flowlayout, but this makes the panels flow horizontally as there is a scroll bar.
How do I make it so I can add panels to the parent panel starting at the top and make them always the same size(or their preferred size).
Also, when I add panels to the parent panel after an event, they do not appear until after I move or resize the form. How do I make it repaint? calling repaint() on it did not work.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/** This lays out components in a column that is constrained to the
top of an area, like the entries in a list or table. It uses a GridLayout
for the main components, thus ensuring they are each of the same size.
For variable height components, a BoxLayout would be better. */
class ConstrainedGrid {
ConstrainedGrid() {
final JPanel gui = new JPanel(new BorderLayout(5,5));
gui.setBorder(new EmptyBorder(3,3,3,3));
gui.setBackground(Color.red);
JPanel scrollPanel = new JPanel(new BorderLayout(2,2));
scrollPanel.setBackground(Color.green);
scrollPanel.add(new JLabel("Center"), BorderLayout.CENTER);
gui.add(new JScrollPane(scrollPanel), BorderLayout.CENTER);
final JPanel componentPanel = new JPanel(new GridLayout(0,1,3,3));
componentPanel.setBackground(Color.orange);
scrollPanel.add(componentPanel, BorderLayout.NORTH);
JButton add = new JButton("Add");
gui.add(add, BorderLayout.NORTH);
add.addActionListener( new ActionListener() {
public void actionPerformed(ActionEvent ae) {
componentPanel.add(new JTextField());
gui.validate();
}
});
Dimension d = gui.getPreferredSize();
d = new Dimension(d.width, d.height+100);
gui.setPreferredSize(d);
JOptionPane.showMessageDialog(null, gui);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ConstrainedGrid cg = new ConstrainedGrid();
}
});
}
}
Assuming JScrollPane, see Sizing a Scroll Pane. For convenience, Scrollable clients such as JTable offer setPreferredScrollableViewportSize(), but you can always set the viewport's size explicitly.

Categories

Resources