GridBagLayout: how to fill vertically using preferred width horizontally - java

I have an issue trying to fill vertically a JScrollPane (which contains a JTable) inside a JPanel: the panel should not be filled horizontally, instead, the JScrollPane should take only the "necessary" space.
I'm using some code found on SO to set the table preferred width according to its content (i'm also overrding JTable's getPreferredScrollableViewportSize method).
This is a screenshot of what i want to achieve:
The JPanel is wider because in my application it will be part of a CardLayout, so the other cards width and height will determine this panel size (in the code below i'm overriding getPreferredSize method for convenience, but i don't want to use it).
This result can be achieved using the below code, you only need to change this line of code:
Object [][] data = new Object [100][1];
to:
Object [][] data = new Object [10][1];
The problem is when the JTable has more rows, in this case the vertical scrollbar will appear, but the scroll pane will "shrink":
This is the code i'm using:
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
public class TableTest
{
public static void main (String [] a) {
SwingUtilities.invokeLater (new Runnable () {
#Override public void run () {
createAndShowGUI ();
}
});
}
private static void createAndShowGUI () {
JFrame frame = new JFrame ("Table Test");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setContentPane (new MainPanel ());
frame.pack ();
frame.setLocationRelativeTo (null);
frame.setVisible (true);
}
}
class MainPanel extends JPanel
{
private static int MAX_HEIGHT = 600;
private static int MAX_WIDTH = 600;
private JTable table;
protected MainPanel () {
super (new GridBagLayout ());
Object [][] data = new Object [100][1];
for (int i = 0; i < 10; i ++) data [i] = new String [] {"Some data ..."};
table = new JTable (data, new String [] {""}) {
#Override public Dimension getPreferredScrollableViewportSize () {
int width = 0;
for (int column = 0; column < getColumnCount (); column ++) width += columnModel.getColumn (column).getPreferredWidth ();
return new Dimension (Math.min (MAX_WIDTH, width), Math.min (MAX_HEIGHT, getRowHeight () * getRowCount ()));
}
};
table.setAutoResizeMode (JTable.AUTO_RESIZE_OFF);
table.setShowGrid (false);
table.setTableHeader (null);
resizeColumns ();
JScrollPane scrollPane = new JScrollPane (table);
scrollPane.setBackground (table.getBackground ());
scrollPane.getViewport ().setBackground (table.getBackground ());
// --- Adding components ---
GridBagConstraints c = new GridBagConstraints ();
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0.0;
c.weighty = 1.0;
add (scrollPane, c);
}
#Override public Dimension getPreferredSize () {
return new Dimension (500, 400);
}
protected void resizeColumns () {
for (int column = 0; column < table.getColumnCount (); column ++) {
TableColumn tableColumn = table.getColumnModel ().getColumn (column);
int width = 0;
for (int row = 0; row < table.getRowCount (); row ++) {
width = Math.max (table.prepareRenderer (table.getCellRenderer (row, column), row, column).getPreferredSize ().width, width);
}
tableColumn.setPreferredWidth (width + table.getIntercellSpacing ().width);
}
}
}
If i set c.fill to GridBagConstraints.BOTH and c.weightx to any positive value, the JScrollPane will fill the parent panel both horizontally and vertically.
How can i let the table's preferred width to be used without manually setting the JScrollPane size?
EDIT
My code is very minimal, since i saw that my problem was not related to the number of table columns. But my real program has to deal with more columns, and a huge number of rows (a million or more in many cases).
A JList was my first try, since i liked the way i could use a prototype value, but it was too slow to load the data (which can also be updated at runtime), all the GUI freezed with more than 200 000 rows, also due to the fact that i'm using a custom renderer.
Also, i already tried to use a BorderLayout, but i really want the scrollpane height to fill my panel, i have tried different solutions, but still i can't solve it.
EDIT2
As #camickr suggests, i could use a BorderLayout for my panel, adding the scrollpane at BorderLayout.WEST to fill the panel vertically wihout taking all the horizontal space. This is the best solution for now, but this will cause the horizontal center alignment to be lost. Any ideas?

(in the code below i'm overriding getPreferredSize method for convenience, but i don't want to use it
I agree. Get rid of the override.
The painting problem when you decrease the height of the frame is because the GridBagLayout will paint the component at its minimum size when there is not enough space.
So, although in general I don't like using any of the set??? methods to hardcode a size the following seems to work:
scrollPane.setMinimumSize(scrollPane.getPreferredSize());
This will keep the scrollpane width from decreasing.
If you don't like playing with hardcoded sizes you could use the Relative Layout. This will easily allow you to center a component.
Then basic code would be:
RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS);
rl.setFill( true );
setLayout(rl);
...
add(Box.createHorizontalGlue(), new Float(1));
add(scrollPane);
add(Box.createHorizontalGlue(), new Float(1));

Your layout problem is caused by your GridBagConstraints (especially fill and weightx)
which prevented the scrollPane from using the available horizontal space.
Instead of
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.VERTICAL;
c.weightx = 0.0;
c.weighty = 1.0;
add (scrollPane, c);
you should use
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH; // !!
c.weightx = 1.0; // !!
c.weighty = 1.0;
add (scrollPane, c);
Then it will look like this:

Related

How can I change the size of JPanels in a JFrame?

I have a JFrame which contains at the moment 4 panels. One of these panels is a keyboard and the other 3 frames are control panels for the keyboard as seen in the following picture:
I have been trying to find a way to shrink the width of the control panels with GridLayout so that they dont take up the whole width of the frame but unfortnunately my code is not working right. Addionally I would like to push the piano roll at the bottom of the screen. This is my code so far:
JFrame frame = new JFrame();
JPanel container = new JPanel();
container.setSize(1000, 1000);
container.setLayout(new GridLayout(5,5));
// add control panels to JPanel
int y = 0;
for(int i =0; i < oscillators.length; ++i) {
JPanel panel = new JPanel();
panel.setSize(289, 100);
panel.setBorder(Utils.WindowDesign.LINE_BORDER);
panel.setLayout(null);
container.add(oscillators[i]);
y+=105;
}
// add keyboard to panel
keyboard = new PianoKeys(this);
keyboard.setFocusable(true);
container.add(keyboard);
// make frame visible
frame.setSize(1000, 1000);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setBackground(Color.WHITE);
frame.setLayout(new BorderLayout());
frame.add(container);
frame.setVisible(true);
Could someone perhaps explain to me why this doesnt work as expected? I am setting the width of the control panels to 289 and my grid contains 5 columns and 5 rows so I am not sure why the control panels end up expanding to the whole width. I appreciate any help you can provide!
SetPreferredSize is normally used for layout negotiating. In your case however SetMaxSize on the panel.
Do not normally use null layouts.
What happened is that the default layout manager of the JFrame is a BorderLayout. This enlarges the content to the JFrame's content pane.
I am not sure whether a GridLayout is what you want. A GridBagLayout would also allow grid cells to merge. There are layouts which allow you to anchor an element to the bottom. In GridBagLayout one can use a constraint to align a smaller-than-cell component to the SOUTH (bottom). That would be with a gap.
One remark:
I am in favor of an other coding style, having one MyJFrame class with components inside. And doing:
public static void main(String[] args) {
MyJFrame frame = new MyJFrame();
SwingUtilities.invokeLater(() -> frame.setVisible());
}
This allows to use a GUI designer (=code generator), which saves a lot of experimenting, even for an experienced programmer.
Interesting question. Therefore, I did some research.
One idea to solve: Switch from GridLayout to BoxLayout
Quoting from Documentation:
"The BoxLayout class puts components in a single row or column. It respects the components' requested maximum sizes and also lets you align components. For further details, see How to Use BoxLayout."
A Visual Guide to Layout Managers
Short Example:
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class GridLayoutExample extends JFrame {
GridLayoutExample(){
JPanel listPane = new JPanel();
//listPane.setLayout(new GridLayout(5,0));
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
this.add(listPane);
for(int i = 0; i < 5; i++){
JLabel imgLabel = new JLabel(generateImage(20 + 20 * i, 10 + 10 * i));
listPane.add(imgLabel);
}
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
public static void main(String[] args) {
new GridLayoutExample();
}
private static ImageIcon generateImage(int width, int height){
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//create random image pixel by pixel
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
int a = (int)(Math.random()*256); //alpha
int r = (int)(Math.random()*256); //red
int g = (int)(Math.random()*256); //green
int b = (int)(Math.random()*256); //blue
int p = (a<<24) | (r<<16) | (g<<8) | b; //pixel
img.setRGB(x, y, p);
}
}
return new ImageIcon(img);
}
}
You can compare GridLayout and BoxLayout by changing the following lines:
//listPane.setLayout(new GridLayout(5,0));
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
Showing the difference - GridLayout:
BoxLayout:
I hope this is helpful.
Credits for random picture generation to dyclassroom.com

GridBagLayout - Trouble placing components without anchor

I'm making an assignment planner program. For the containers (the assignments), I want to place certain components into certain places. However. I can only seem to move components around by using anchor. It seems as if my gridx and gridy do nothing. Could anyone point out my problem and possibly offer some suggestions. My code and a picture of the intended final result are provided below.
Code:
import javax.swing.*;
import java.awt.*;
import static java.awt.GridBagConstraints.*;
//import java.util.ArrayList;
public class MyWindow
{
private final JFrame frame = new JFrame();
private final int WINDOW_WIDTH = 500, WINDOW_DEPTH = 500;
private JPanel panel;
private JPanel toDoList, completed;
private int scrollPaneValue = 110;
//ArrayList<JFrame> frame = new ArrayList<>();
public MyWindow()
{
frame.setTitle("Assignment Planner");
this.contents();
}
private void contents()//make a border above each panel stating "TO-DO" or "COMPLETED"
{//use an arraylist to create containers ArrayList<JPanel> container = new ArrayList<>();
frame.setSize(WINDOW_WIDTH, WINDOW_DEPTH);
panel = new JPanel(new GridLayout(2, 1));
toDoList = new JPanel();
toDoList.setLayout(new /*GridLayout(0,1,5,5)*/BoxLayout(toDoList, BoxLayout.PAGE_AXIS));
toDoList.setPreferredSize(new Dimension(250, 110));
panel.add(toDoList);
completed = new JPanel();
//panelCompleted.setLayout(new GridLayout(0, 1)); //fix like one above
panel.add(completed);
JScrollPane scroll = new JScrollPane(toDoList);
panel.add(scroll); //scroll panes for both panels
JScrollPane scroll2 = new JScrollPane(completed);
panel.add(scroll2);
toDoList.add(Box.createRigidArea(new Dimension(0,1)));
toDoList.add(assignment());
scrollPaneValue += 110; //add these 2 lines of code, beginning after the first two containers to increase jscrollpane
toDoList.setPreferredSize(new Dimension(250, scrollPaneValue));
//toDoList.revalidate(); may not even need
frame.getContentPane().add(panel, BorderLayout.CENTER);//add the panel in the JFrame's content pane in the center
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
JPanel assignment()
{
JPanel container = new JPanel(new GridBagLayout());
container.setMaximumSize(new Dimension(500,100));
GridBagConstraints cDefault = new GridBagConstraints();
cDefault.weightx = 0.5;
cDefault.insets = new Insets(5,5,5,5);
JCheckBox cb;
JLabel dueDate, description;
JButton edit;
cb = new JCheckBox();
GridBagConstraints cCb = new GridBagConstraints();
cCb.weightx = 0.5;
cCb.weighty = 1;
cCb.fill = GridBagConstraints.NONE;//originally none
cCb.gridx = 0;
cCb.gridy = 0;
//cCb.gridwidth = 1;
//cCb.gridheight = 1;
cCb.anchor = FIRST_LINE_START;//may not need, plus needs static import
cCb.insets = cDefault.insets;
cb.setBackground(Color.RED);
container.add(cb, cCb);
dueDate = new JLabel("Due Date");
GridBagConstraints cDueDate = new GridBagConstraints();
cDueDate.gridx = 1;
cDueDate.gridy = 0;
cDueDate.gridwidth = 2;
//cDueDate.fill = GridBagConstraints.HORIZONTAL;
cDueDate.anchor = PAGE_START;
dueDate.setBackground(Color.BLUE);
//cDueDate.anchor = FIRST_LINE_START;
container.add(dueDate, cDueDate);
edit = new JButton("Edit");
GridBagConstraints e = new GridBagConstraints();
e.gridx = 4;
e.gridy = 0;
e.anchor = GridBagConstraints.FIRST_LINE_END;
e.insets = cDefault.insets;
edit.setBackground(Color.GREEN);
container.add(edit, e);
description = new JLabel("Description...");
GridBagConstraints d = new GridBagConstraints();
d.gridx = 1;
d.gridy = 3;
d.gridwidth = 3;
d.fill = GridBagConstraints.HORIZONTAL;
description.setBackground(Color.YELLOW);
container.add(description, d);
container.setBackground(Color.LIGHT_GRAY);//does no fill area behind checkbox
return container;
}
}
What I want the container to look like:
This just concerns my lack of knowledge with GridBagLayout.
Start with the section from the Swing tutorial on How to Use GridBagLayout for working examples and an explanation of all the constraints.
A few things about the code:
//container.setMaximumSize(new Dimension(500,100));
Don't set a maximum size. The layout manager will determine the size of the panel.
//cCb.weightx = 0.5;
//cCb.weighty = 1;
The above code assigns all the extra space of the panel to the check box, since it is the only component with a weightx/y constraint. I doubt you want that. Try commenting out those statements to see what happens.
dueDate.setOpaque(true);
dueDate.setBackground(Color.BLUE);
Yes, that is a good idea to set a background to see the actual size of the component. Problem is a JLabel is transparent by default so the background is never painted. You need need to make your labels opaque if you want to see the background. Another approach to help with debugging is to add LineBorder to the label, then you don't need to worry about transparency.
//e.gridx = 4;
e.gridx = 3;
Be careful with the grid value. You can't just use an value. The first component has a width of 1 and the second a width of 2, so the this component should start at 3.
I can only seem to move components around by using anchor
Actually, I don't this the anchor is doing anything. As I understand it, the anchor only has meaning when the component size is smaller than the grid size. In your original code, only the check box used weightx/y values, so that is the only component where this is true.
The GridBagLayout is one of the most complicated (and flexible) layout manager to use, so yes it takes practice to learn how to use it. Reread the tutorial and play with the constraints.

What settings should I use to resolve this GridBagLayout resizing issue?

I have been searching and trying ways to resolve the problems I encountered with GridBagLayout resizing but to no avail. The problems are:
I drag the bottom of the JDialog window down to make it taller; the search panel (above panel in the below screenshot) kept getting taller, just adding white space that it didn't need for its contents (I don't want this wasted space which takes height away from the scrolling table below it).
If I move the right side of the window to make it a bit narrower, the scroll panel becomes too short even though my screen and the window could have displayed its full height; a vertical scroll bar appears (I don't like the panels to scroll vertically, just horizontally.)
My JDialog that holds below components in the upper right portion uses below layout manager and constraints:
JPanel level 1 uses GridBagLayout Setting 1 (see below)
JScrollPane level 2 uses GridBagLayout Setting 1
JPanel level 3 uses GridBagLayout Setting 2
JPanel level 4 uses GridBagLayout Setting 2
JPanel level 5 uses GridBagLayout Setting 2
JPanel level 5 uses GridBagLayout Setting 2
GridBagLayout.GridBagConstraints (c)
Setting 1:
c.fill = GridBagConstraints.BOTH;
c.weightx = 99.0;
c.weighty = 99.0;
Setting 2:
c.fill = GridBagConstraints.NONE;
c.weightx = 1.0;
c.weighty = 1.0;
Originally, this is my JDialog if I don't use JScrollPane:
If I drag the window to make it bigger, the size of the JPanel (which was what I use before I used JScrollPane to make it scrollable horizontally) remains as it is. I want to achieve the same behavior, only that I want to make it horizontally scrollable.
What layout manager should I use to resolve this resizing problem? If I have to really use GridBagLayout, what constraints/settings should I use? I would highly appreciate any suggestion. Thank you!
UPDATE 1 ------------------------------------------------------
Kindly check sample code scenario below. Both scrollable panels (RED and BLUE) use GridBagConstraints.BOTH, weightx and weighty equal to 99.0. I want the red panel to have a height that is enough to contain the text field. I also want that when the dialog is dragged down to make the window bigger, the red panel's height remains the same. Please note that in the actual screen, both panels' number of contents may change (add/minus buttons exist), hence, setting of minimum panel size (for resizing) won't help. Kindly advise how I should go about this case. Thanks!
package cfr.view.search;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
public class GBLSample extends JDialog {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GBLSample frame = new GBLSample();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public GBLSample() {
setBounds(100, 100, 350, 400);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
contentPane.setLayout(gbl_contentPane);
// RED PANEL ------------------------------------------------
JScrollPane sp1 = new JScrollPane();
GridBagConstraints gbc1 = new GridBagConstraints();
gbc1.fill = GridBagConstraints.BOTH;
gbc1.weightx = 99.0;
gbc1.weighty = 99.0;
contentPane.add(sp1, gbc1);
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.RED);
JTextField field = new JTextField(20);
GridBagConstraints gbc2 = new GridBagConstraints();
gbc2.anchor = GridBagConstraints.PAGE_START;
gbc2.fill = GridBagConstraints.NONE;
contentPane.add(field, gbc2);
redPanel.add(field);
sp1.setViewportView(redPanel);
// BLUE PANEL ------------------------------------------------
JScrollPane sp2 = new JScrollPane();
GridBagConstraints gbc3 = new GridBagConstraints();
gbc3.fill = GridBagConstraints.BOTH;
gbc3.gridx = 0;
gbc3.gridy = 1;
gbc3.weightx = 99.0;
gbc3.weighty = 99.0;
contentPane.add(sp2, gbc3);
JPanel bluePanel = new JPanel();
bluePanel.setBackground(Color.BLUE);
sp2.setViewportView(bluePanel);
}
}
UPDATE 2 ------------------------------------------------------
Still have the same problem with the JPanel's sizing as #1 above.
Problem 1
try this code:
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.EmptyBorder;
public class GridBagLayoutSample extends JFrame {
private JPanel contentPane;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
GridBagLayoutSample frame = new GridBagLayoutSample();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public GridBagLayoutSample() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[]{0, 0};
gbl_contentPane.rowHeights = new int[]{50, 0, 0};
//the 1.0 value in columnWeights let redPanel and scrollPane (that contain bleuPanel) grow horizontally
//the 1.0 value is in the first index so it affect the first column
//you still need to use a valid value for gbc_redPanel.fill and gbc_scrollPane.fill;
gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
//the 0.0 value of rowWeights => components in first row will not grow
//the 0.0 value of rowWeights => components in second row will grow
gbl_contentPane.rowWeights = new double[]{0.0, 1.0, Double.MIN_VALUE};
contentPane.setLayout(gbl_contentPane);
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.RED);
GridBagConstraints gbc_redPanel = new GridBagConstraints();
gbc_redPanel.insets = new Insets(0, 0, 5, 0);
gbc_redPanel.fill = GridBagConstraints.BOTH;
gbc_redPanel.gridx = 0;
gbc_redPanel.gridy = 0;
contentPane.add(redPanel, gbc_redPanel);
JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 1;
contentPane.add(scrollPane, gbc_scrollPane);
JPanel bleuPanel = new JPanel();
bleuPanel.setBackground(Color.BLUE);
//scrollpane work with the preferred size of the component in the viewportView
//try to change this values
bleuPanel.setPreferredSize(new Dimension(400, 400));
scrollPane.setViewportView(bleuPanel);
}
}
add this to your code
gbl_contentPane.rowHeights = new int[]{40, 0, 0};
gbl_contentPane.rowWeights = new double[]{0.0,0.0, 1.0, Double.MIN_VALUE};
gbc1.gridx = 0;
gbc1.gridy = 0;
and remove this
gbc1.weightx = 99.0;
gbc1.weighty = 99.0;
Maybe a GridBagLayout might not be suited for your current UI, and a BorderLayout would work better? Put the red panel in BorderLayout.NORTH and put the blue panel in BorderLayout.CENTER. Components in the CENTER region will grow and shrink as you resize your dialog, and your red panel's preferredSize will be respected as you +/- items.
I have been searching and trying ways to resolve the problems I encountered with GridBagLayout resizing but to no avail.
Welcome to GridBagLayout.
First thing to note is that in
JPanel redPanel = new JPanel();
redPanel.setBackground(Color.RED);
JTextField field = new JTextField(20);
// GridBagConstraints gbc2 = new GridBagConstraints();
// gbc2.anchor = GridBagConstraints.PAGE_START;
// gbc2.fill = GridBagConstraints.NONE;
// contentPane.add(field, gbc2);
redPanel.add(field);
sp1.setViewportView(redPanel);
You are adding the text field to the content pane and then adding it again to the panel, which overrides your previous assignment (a component can only belong to one container). I commented out the lines which do nothing. The choice for how to lay out the text field in the red panel is completely different than the choice of how to lay out the red panel in the content pane.
I want the red panel to have a height that is enough to contain the text field.
That is taken care of by the layout manager. However, if you resize the window manually so that the panel will not have enough space, then scrollbars will appear provided that the panel is in a JScrollPane. You can try to #Override the getMinimumSize of the panel to its getPreferredSize, but not all layout managers respect it, I believe.
Regardless, I don't see why you need the scroll pane for the red panel at all.
I also want that when the dialog is dragged down to make the window bigger, the red panel's height remains the same.
Then you should set its weighty to 0.
Note: don't use underscores (_) in non-final variable names in accordance with the Java naming conventions (gbl_contentPane should be gblContentPane).
Edit
Here is an example of of dynamically changing the allocated size for the redPanel. I use BorderLayout for the content pane (also suggested by Dan O) for simplicity.
public class GBLSample extends JDialog {
private JPanel contentPane;
private JScrollPane redSP = new JScrollPane(new RedPanel());
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
GBLSample frame = new GBLSample();
frame.setVisible(true);
});
}
private class RedPanel extends JPanel {
RedPanel() {
setLayout(new GridBagLayout());
setBackground(Color.RED);
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.FIRST_LINE_START;
add(new JTextField(10), c);
c.weightx = 1;
c.gridx = 1;
add(new JComboBox<>(), c);
c.weightx = 0;
c.gridx = 0;
add(new JComboBox<>(), c);
c.gridx = 1;
add(new JComboBox<>(), c);
c.weighty = 1;
c.gridx = 0;
c.gridy = 2;
add(new JButton("SSSS"), c);
}
#Override
public Dimension getPreferredSize() {
Dimension dim = super.getPreferredSize();
JScrollBar sb = redSP.getHorizontalScrollBar();
if (!sb.isShowing())
return dim;
return new Dimension(dim.width, dim.height + sb.getHeight());
}
}
public GBLSample() {
setBounds(100, 100, 350, 400);
contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
// RED PANEL ------------------------------------------------
redSP.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
contentPane.add(redSP, BorderLayout.PAGE_START);
// BLUE PANEL ------------------------------------------------
JPanel bluePanel = new JPanel();
bluePanel.setBackground(Color.BLUE);
contentPane.add(new JScrollPane(bluePanel));
}
}
The idea is to add all extra space at the bottom of the panel by giving all the weight to a component on the bottom (and similarly can be done for the x axis with a right-most component). When the scrollbar is shown, the panel recalculates its height and adds the scrollbar's height to to itself (to the bottom most component), then the scrollbar does not hide anything.
Unfortunately, there was a bug in the system's overridden JPanel.getMinimumSize() that was causing my search panel to get squished out of existence. I was able to find a fix for it already without changing any setting with the system's customized layout manager. Still, thanks a lot for all your answers and help.

Moving Jtable in Java, Swing, Flowlayout

I have this code so far:
public class Table extends JFrame {
JTable table;
public Table()
{
setLayout (new FlowLayout()); //Default layout
String[] columnNames = {"Fly model", "Fly kode",
"Destination", "Tidspunkt"};
Object[][] data = {
{"Boeing 737", "Ab79SO", "Oslo", "22:00"},
{"MD125", "Tb682O", "Stockholm", "15:21"},
{"Boeing 737", "HJ72SR", "Reikjavic", "08:13"},
};
table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 50));
table.setFillsViewportHeight(true);
setVisible(true);
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
public JTable returnJTable()
{
setVisible(false);
return table;
}
}
I am not used to use FlowLayout, and I therefore don't know how to move this object around in the JFrame that I am using. I know that when you use a null (absolute) layout, you can use setBounds() to tell the JFrame where to position the elements. But how do I do that in FlowLayout?
You can't do that with FlowLayout. You can add new components horizontally or vertically one by another, but you can't to add component to specific position. You can try to use some tricks with blank panels or labels for spaces before/after your JTable, but better to use another layout.
Try to use BorderLayout, it's simple and with help of that, you can positioning your JTable in different places. Read tutorial for that.
Or you can use another LayoutManager, read about them and choose.
With FlowLayout you can't move the object around. All objects are placed in one line.
Try to use the BorderLayout or GridBagLayout.
Here's a visual guide to Layout Managers.
Panel myTable = new Panel(new GridBagLayout());
GridBagConstraints c1 = new GridBagConstraints();
c1.fill = GridBagConstraints.HORIZONTAL; //area
c1.ipadx = 0; //spacing
c1.ipady = 0; //spacing
c1.weightx = 1.0; //horizontal
c1.weighty = 1.0; //vertical
c1.anchor = GridBagConstraints.CENTER; //orientation
c1.insets = new Insets(10,10,10,10); //padding
c1.gridx = 0; //column
c1.gridy = 0; //line
c1.gridheight = 1; //number of lines
c1.gridwidth = 1; //number of columns
myTable.add(new JScrollPane(table),c1);
You can move your table if you change the orientation.

Fit JTable to fill the JPanel

Hi here are my codes for my table settings:
String [] column = {"MacAddress","PcName","OperatingSystem","IpAddress","Port","Status"};
model = new DefaultTableModel(0,column.length);
model.setColumnIdentifiers(column);
mainTable = new JTable(model);
mainTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
pane = new JScrollPane(mainTable);
pnlTabel = new JPanel();
pnlTabel.setBorder(BorderFactory.createTitledBorder(""));
pnlTabel.setPreferredSize(new Dimension(dim.width*70/100, dim.height*60/100));
pnlTabel.add(pane);
addMainPanel(pnlTabel);
Here is my addMainPanel() function:
public void addMainPanel(Component pnl){
mainPanel.add(pnl);
mainPanel.revalidate();
}
And here is my code for my mainPanel:
mainPanel = new JPanel();
mainPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
add(mainPanel,"Center");
and I'm using border layout for my frame:
setLayout(new BorderLayout(0,0));
My problem is that, even i use this set of code to set my JTable to fit but it seems to fail all the this, this code:
mainTable.setAutoResizeMode(JTa![enter image description here][1]ble.AUTO_RESIZE_OFF);
for(int i=0;i<=column.length-1;i++){
mainTable.getColumnModel().getColumn(i).setPreferredWidth(300);
}
When is use that code, my jtable does not resize but only add on a horizontal scroll bar at the bottom.
No offense meant but .. your code and consequently your question is a mess ;-) Plus you don't explain what exactly you want to achieve.
Trying to detangle, taking the nested layouts/resizing characteristics (as seen in the snippets, might not be complete):
frame // BorderLayout
mainPanel // FlowLayout
pnlTabel // FlowLayout, hard-coded prefSize
pane // scrollPane
mainTable // auto-resize-off
Aside: intentionally kept untelling names to demonstrate how mixing naming schemes tend to contribute to overall confusion :-) Doesn't really matter whether you decide for pre or postFixing some type-related marker, but if you do be consistent.
In that hierarchy, there are two levels of FlowLayout which basically will layout their children at their respective prefs and adjusting their own pref accordingly, lest the pref were hard-coded on the level of the pnlTable: however the table's pref will be changed (by changing the column prefs) it cannot bubble further up because ... hard-coding the pref leads not calculating its size (neither by layoutManager and nor uiDelegate, both never get a chance to do it)
Another issue - the more interesting one :-) - is that the JScrollPane is somewhat special in
calculating its own prefSize from its view's pref/scrollablePrefViewportSize depending on whether or not the view implements Scrollable (JTable does so, though in a crappy way)
being a validationRoot: invalidating the view (or any children) doesn't bubble further up the hierarchy
Assuming that you want the table's scrollPane to grow if the prefWidts of the columns change, there are two thingies to tweak:
implement table's getPreferredScrollableWidth to return a value calculated based on the prefWidth of the columns
revalidate a container higher up in the hierarchy
Some code to play with:
final JTable table = new JTable(50, 10) {
// properties to base a reasonable prefScrollable size
int visibleColumns = 3;
int visibleRows = 10;
// hard-coded default in super
Dimension dummySuper = new Dimension(450, 400);
#Override
public Dimension getPreferredScrollableViewportSize() {
Dimension dim = super.getPreferredScrollableViewportSize();
if (!dummySuper.equals(dim)) return dim;
dim = new Dimension();
for (int column = 0; column < Math.min(visibleColumns, getColumnCount()); column++) {
dim.width += getColumnModel().getColumn(column).getPreferredWidth();
}
dim.height = visibleRows * getRowHeight();
return dim;
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
for (int i = 0; i < table.getRowCount(); i++) {
table.setValueAt("row: " + i, i, 0);
}
JComponent tablePanel = new JPanel();
tablePanel.add(new JScrollPane(table));
Action sizeColumns = new AbstractAction("size columns") {
int prefWidth = 75;
#Override
public void actionPerformed(ActionEvent e) {
int newWidth = prefWidth + 15;
for (int i = 0; i < table.getColumnCount(); i++) {
if (table.getColumnModel().getColumn(i).getPreferredWidth() == prefWidth)
table.getColumnModel().getColumn(i).setPreferredWidth(newWidth);
}
prefWidth = newWidth;
// revalidate "higher up" than the table itself
frame.revalidate();
}
};
frame.add(new JButton(sizeColumns), BorderLayout.SOUTH);
If you want a JTable to fill the available space, you should put it inside a JPanel which has a BorderLayout layout manager. Also don't forget about the JScrollPane which ensures that if the table doesn't fit into the view (e.g. too many rows), scrollbars will appear:
JFrame frame = new JFrame();
// set up frame
JTable table = new JTable();
// Set up table, add data
// Frame has a content pane with BorderLayout by default
frame.getContentPane().add( new JScrollPane( table ), BorderLayout.CENTER );
If you have other content you wish to display besides the table, you can add those to the NORTH, SOUTH, EAST, WEST parts of the content panel (which can be wrapped into other panels if more components are to be placed there).

Categories

Resources