Add JPanels dynamically to either a JTable or a JScrollPane - java

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.

Related

Java - How to change components height using BoxLayout?

So, I have a tiny GUI program and I decided to use the BoxLayout to display the components from top to bottom. Everything works fine but I'm not able to change the height of my JButtons. I tried many things like setPreferredSize() but then i had the problem that the width isn't correct, as well. Using setMaximumSize() sets the width like i want to but the height still doensn't change. Maybe some of you could help me :) Thanks
public class SimpleSkinViewer extends JPanel implements ActionListener{
private final Dimension boxDimension = new Dimension(320, 320);
private final Dimension buttonDimension = new Dimension(320, 60);
private final Dimension spaceDimension = new Dimension(0, 5);
private JLabel imagebox;
private JButton loadButton;
private JButton changeButton;
private JButton downloadButton;
public SimpleSkinViewer() {
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
imagebox = new JLabel("");
imagebox.setIcon(new ImageIcon(loadImage("http://skins.minecraft.net/MinecraftSkins/AvarionDE.png")));
loadButton = new JButton("Load Skin");
changeButton = new JButton("Change Skin");
downloadButton = new JButton("Download");
//add listeners
loadButton.addActionListener(this);
changeButton.addActionListener(this);
downloadButton.addActionListener(this);
//dimensions
imagebox.setMaximumSize(boxDimension);
loadButton.setMaximumSize(buttonDimension);
changeButton.setMaximumSize(buttonDimension);
downloadButton.setMaximumSize(buttonDimension);
add(imagebox);
add(Box.createRigidArea(spaceDimension));
add(loadButton);
add(Box.createRigidArea(spaceDimension));
add(changeButton);
add(Box.createRigidArea(spaceDimension));
add(downloadButton);
}
#Override
public void actionPerformed(ActionEvent arg0) {
}
//and other stuff.....
public static void main (String[] args) {
JFrame frame = new JFrame("Avarion's Simple Skin Viewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new SimpleSkinViewer());
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
}
You need Box.createVerticalGlue()
Change
add(changeButton);
add(Box.createRigidArea(spaceDimension));
with
add(changeButton);
add(Box.createVerticalGlue());
Then you can use .setPreferredSize(new Dimension(x,y)); and buttons will adapt to your layout
From the docs for BoxLayout
When a BoxLayout lays out components from top to bottom, it tries to
size each component at the component's preferred height.
For a top-to-bottom box layout, the preferred width of the container
is that of the maximum preferred width of the children. If the
container is forced to be wider than that, BoxLayout attempts to size
the width of each component to that of the container's width (minus
insets). If the maximum size of a component is smaller than the width
of the container, then X alignment comes into play.
So, you can set both the maximumSize and preferredSize to get the desired size.
loadButton.setMaximumSize(buttonDimension);
loadButton.setPreferredSize(buttonDimension);

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));
}

Changing layout of a JPanel sent to JOptionPane with the pane still running

Trying to change the look of a JOptionPane while its open, depending on which radiobutton the user clicks. What am I doing wrong? It works perfect if I for example add a button and move a JLabel from side to side of the window.
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import static javax.swing.JOptionPane.*;
public class ChangePanel extends JFrame{
private JButton click = new JButton("CLICK ME!");
ChangePanel(){
add(click, BorderLayout.SOUTH);
click.addActionListener(new ButtonListen());
setVisible(true);
setSize(300,100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public class ButtonListen implements ActionListener{
public void actionPerformed(ActionEvent e){
PopUpPanel pop = new PopUpPanel();
showConfirmDialog(ChangePanel.this, pop, "Changeable", OK_CANCEL_OPTION);
}
}
//Send this as Parameter to the ConfirmDialog
public class PopUpPanel extends JPanel implements ActionListener{
JRadioButton jewelry = new JRadioButton("Jewelry");
JRadioButton shares = new JRadioButton("Shares");
JRadioButton machine = new JRadioButton("Machine");
PopUpPanel(){
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
ButtonGroup bg = new ButtonGroup();
JPanel north = new JPanel();
bg.add(jewelry);
jewelry.addActionListener(this);
bg.add(shares);
shares.addActionListener(this);
bg.add(machine);
machine.addActionListener(this);
north.add(jewelry);
north.add(shares);
north.add(machine);
add(north);
}
//Listener for RadioButtons
public void actionPerformed(ActionEvent e){
JTextField info1Txt = new JTextField(12);
JTextField info2Txt = new JTextField(12);
JTextField info3Txt = new JTextField(3);;
JRadioButton b = (JRadioButton)e.getSource();
if(b.getText().equals("Jewelry")){
//Dummy test text
System.out.println("Jewelry");
JPanel info1 = new JPanel();
info1.add(new JLabel("info1:"));
info1.add(info1Txt);
add(info1);
JPanel info2 = new JPanel();
info2.add(new JLabel("info2:"));
info2.add(info2Txt);
add(info2);
JPanel info3 = new JPanel();
info3.add(new JLabel("info3:"));
info3.add(info3Txt);
add(info3);
validate();
repaint();
}else if(b.getText().equals("Shares")){
//Dummy test text
System.out.println("Shares");
}else
//Dummy test text
System.out.println("Machine");
}
}
public static void main(String[] args){
new ChangePanel();
}
}
As you are working with BoxLayout, you should provide size hints to the PopUpPanel panel, which you haven't given.
When a BoxLayout lays out components from top to bottom, it tries to size each component at the component's preferred height. If the vertical space of the layout does not match the sum of the preferred heights, then BoxLayout tries to resize the components to fill the space. The components either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of each of the components.
check out the official tutorial page discussion: BoxLayout Feature
Call revalidate() and repaint() on the container after removing or adding components to it. So if you change the following lines:
validate();
repaint();
to:
revalidate();
repaint();
The content should appear. Though, it will not fit the original size of the JOptionPane. You can override PopUpPanel.getPreferredSize() to return desired size so that JOptionPane is packed properly, ie:
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
You can also use JDialog instead of JOptionPane.
Also, consider using CardLayout instead of swapping components manually. Check How to Use CardLayout for examples.
Why not just use setPreferredSize(new Dimension(300, 300)) in PopUpPanel constructor? Works fine for me. Good eye on revalidate and repaint.

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.

Swing: set JFrame content area size

I'm trying to make a JFrame with a usable content area of exactly 500x500. If I do this...
public MyFrame() {
super("Hello, world!");
setSize(500,500);
}
... I get a window whose full size is 500x500, including the title bar, etc., where I really need a window whose size is something like 504x520 to account for the window border and titlebar. How can I achieve this?
you may try couple of things:
1 - a hack:
public MyFrame(){
JFrame temp = new JFrame;
temp.pack();
Insets insets = temp.getInsets();
temp = null;
this.setSize(new Dimension(insets.left + insets.right + 500,
insets.top + insets.bottom + 500));
this.setVisible(true);
this.setResizable(false);
}
2- or
Add a JPanel to the frame's content pane and
Just set the preferred/minimum size
of the JPanel to 500X500, call pack()
2- is more portable
Simply use:
public MyFrame() {
this.getContentPane().setPreferredSize(new Dimension(500, 500));
this.pack();
}
There's no need for a JPanel to be in there, if you just want to set the frame's size.
Never mind, I figured it out:
public MyFrame() {
super("Hello, world!");
myJPanel.setPreferredSize(new Dimension(500,500));
add(myJPanel);
pack();
}

Categories

Resources