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).
Related
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:
EDIT: Sorry for many edits. I forgot what I wrote by myself.
I use JPanel that has BoxLayout as root Panel for JFrame. I'm adding to this root Panel two other Panels: buttonPanel with FlowLayou and tabbedPane. Each tabbed Pane is created dynamically by pressing second button at the top. In tabbedPane there is a templatePanel with BoxLayout that contains three other general JPanels: Checkboxes Panel with FlowLayout, tablePanel with BorderLayout and another one with BoxLayout.
I'm adding a JTable to tablePanel with BoderLayout.CENTER and after running program JTable is way too big vertically and I need to resize frame. I need to add rows dynamically so I create an empty JTable with my custom DefaultTableModel (I overloaded isCellEditable method, nothing more) and then by checking checkboxes I fill it with data.
JTable is also way too big than maximum rows number it is designed to hold.
What I mean:
How can I shrink it?
I create templatePanel with class's constructor (extends JPanel) by just add.(templatePanel)
code:
public TemplatePanel()
{
model = new DefaultTableModel(new Object[][] {}, new String[]
{"<html>...</html>", "<html>...</html>",
"...", "...", "<html>...</html>",
"<html>...</html>", "...", "...",
"<html>...</html>"})
{
#Override
public boolean isCellEditable(int row, int column)
{
return column == 1 || column == 3;
}
};
templatePanel = new JPanel();
tablePanel = new JPanel();
templatePanel.setLayout(new BoxLayout(templatePanel, BoxLayout.PAGE_AXIS));
tablePanel.setLayout(new BorderLayout());
checkBoxPanel = new JPanel();
checkBoxPanel.setLayout(new FlowLayout());
1 = new JCheckBox("...");
2 = new JCheckBox("...");
3 = new JCheckBox("...");
4 = new JCheckBox("...");
5 = new JCheckBox("...");
6 = new JCheckBox("...");
checkBoxPanel.add(1);
checkBoxPanel.add(2);
checkBoxPanel.add(3);
checkBoxPanel.add(4);
checkBoxPanel.add(5);
checkBoxPanel.add(6);
1.addItemListener(this);
2.addItemListener(this);
3.addItemListener(this);
4.addItemListener(this);
5.addItemListener(this);
6.addItemListener(this);
table = new JTable(model);
scrollPane = new JScrollPane(table);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setTableHeader(new JTableHeader(table.getColumnModel())
{
#Override
public Dimension getPreferredSize()
{
Dimension d = super.getPreferredSize();
d.height = 50;
return d;
}
});
TableColumn firstColumn = table.getColumnModel().getColumn(0);
TableColumn secondColumn = table.getColumnModel().getColumn(1);
TableColumn thirdColumn = table.getColumnModel().getColumn(2);
TableColumn ninthColumn = table.getColumnModel().getColumn(8);
firstColumn.setPreferredWidth(170);
secondColumn.setPreferredWidth(50);
thirdColumn.setPreferredWidth(30);
ninthColumn.setPreferredWidth(100);
table.setRowHeight(30);
tablePanel.add(scrollPane, BorderLayout.NORTH);
templatePanel.add(checkBoxPanel);
templatePanel.add(tablePanel);
add(templatePanel);
}
The basic logic should be:
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane( table );
This will get rid of the extra vertical space.
If I understand correctly, you want the frame of the table to be smaller? Is that correct? If you're using BorderLayout, center will cover the entire frame unless you add something in the cardinal directions. Create a box and add it SOUTH to create a cushion between the bottom and the table.
Again, If I'm not understanding this properly I apologize.
Edit:
Have you tried using setPreferredSize(new Dimension(800, 500)) on the scrollpane?
I'm having an issue creating an empty JTabbedPane where the only portion to be seen on the GUI are the row of tabs.
Everytime I add a new tab with an "empty" component, the height of the JTabbedPane increases, but why?
The current workaround is to override getPreferredSize(), but it seems kludgy to me. Comment out the overridden method to see what I mean.
Am I missing something obvious?
Background:
We need a JTabbedPane where the tabbed pane starts off with 2 tabs, but the user can add more tabs as needed, up to 10. In addition, each tab contains the same components, but with different data. The decision was made to fake the look of a JTabbedPane, by implementing an empty JTabbedPane solely for the look, and to use a single fixed JPanel whose contents will be refreshed based on the tab clicked.
(Normally, I could just recreate the JPanel n-times, but that would nightmarish for the presenter classes who control the UI, which is beyond the scope of my question.)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomTabbedPane implements Runnable
{
static final int MAX_TABS = 11; // includes the "add" tab
JPanel pnlTabs;
JTabbedPane tabbedPane;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new CustomTabbedPane());
}
public void run()
{
JPanel p = buildPanel();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(p);
frame.setSize(800,400);
frame.setVisible(true);
}
private JPanel buildPanel()
{
tabbedPane = new JTabbedPane()
{
#Override
public Dimension getPreferredSize()
{
Dimension dim = super.getPreferredSize();
dim.height = getUI().getTabBounds(this, 0).height + 1;
return dim;
}
};
tabbedPane.addTab("Tab 1", getEmptyComp());
tabbedPane.addTab("Tab 2", getEmptyComp());
tabbedPane.addTab("+", new TabCreator());
tabbedPane.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
addTab();
}
});
JScrollPane scroll = new JScrollPane(new JTable(5,10));
JPanel p = new JPanel(new BorderLayout());
p.add(tabbedPane, BorderLayout.NORTH);
p.add(scroll, BorderLayout.CENTER);
p.setBorder(BorderFactory.createLineBorder(Color.BLUE.darker(), 1));
return p;
}
private void addTab()
{
if (tabbedPane.getSelectedComponent() instanceof TabCreator)
{
int selIndex = tabbedPane.getSelectedIndex();
if (tabbedPane.getComponentCount() < MAX_TABS)
{
if (selIndex == tabbedPane.getComponentCount()-1)
{
String title = "Tab " + (selIndex + 1);
tabbedPane.insertTab(title, null, getEmptyComp(), "", selIndex);
tabbedPane.setSelectedIndex(selIndex);
if (tabbedPane.getComponentCount() == MAX_TABS)
{
tabbedPane.setEnabledAt(MAX_TABS-1, false);
}
}
}
}
}
private Component getEmptyComp()
{
return Box.createVerticalStrut(1);
}
class TabCreator extends JLabel {}
}
Great question! But it's fairly straightforward to get a hint on what's happening.
The problem is that your content does not have a minimum width, preferred size is not set, tab placement is top/bottom and the UI is default.
Since preferred size is not set, then when the layout is revalidated the calculations of space required go into the BasicTabbedPaneUI method Dimension calculateSize(false).
That reads:
int height = 0;
int width = 0;
<other vars>
// Determine minimum size required to display largest
// child in each dimension
<actual method>
Here it calculates the minimum size to accommodate any child and stores it into height/width. In your case this yields something like 10,10 (because of the single Label tab creator I think, I didn't follow that one).
Then happens the magic:
switch(tabPlacement) {
case LEFT:
case RIGHT:
height = Math.max(height, calculateMaxTabHeight(tabPlacement));
tabExtent = preferredTabAreaWidth(tabPlacement, height - tabAreaInsets.top - tabAreaInsets.bottom);
width += tabExtent;
break;
case TOP:
case BOTTOM:
default:
width = Math.max(width, calculateMaxTabWidth(tabPlacement));
tabExtent = preferredTabAreaHeight(tabPlacement, width - tabAreaInsets.left - tabAreaInsets.right);
height += tabExtent;
}
What happens here is it sets the preferred width to be the maximum of the largest tab width and the largest child width. In your case it's around 44 for the tab text. The tabExtent is then calculated to see just how many rows of tabs are needed to support this preferred width. In your case - it's 1 extra row of tabs for each tab. That's where the extra height in preferredSize().height comes from. Essentially because for horizontal tab placement it cares about width first, then height.
How to fix:
Set a preferred size :) I know a lot of people say don't set the preferred size, but in this case this will just work. Since a preferred size is set (via actually setting it, not overriding getPreferredSize()), the code will never get to counting tabs.
Give at least one of your children a size (via setPreferredSize or overriding getPreferredSize). If one of the childrens width is that of the frame, or, say, the table at the bottom the TabbedPane will not be allocating an extra row for each tab, since a single row will fit everything.
Make your own UI for the tabbed pane. It may be easier to make your own tabbed pane though really, I've never done this.
EDIT:
After thinking about this a bit more, I realized that solution number 1 AND your own solution suffer from the flaw that, if the tabbed pane actually does require multiple rows for the tabs (hello frame resizes), bad things will happen. Don't use it.
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.
I have some tables which should draw from right to left and top to bottom at the frame. Right now I used absolute layout and working with coordination. Is there any BoxLayout or any other Java layout can do it? I should mention that the number of tables is dynamic.
My second question is how can I dock these tables to frame? I mean I want when the frame resize, tables keep their positions on the screen.
Most layout managers will respect the orientation of the component:
panel.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT );
panel.add(...);
Or you can always just add the components to the beginning of the container
panel.add(component1, 0);
panel.add(component2, 0);
You may want to use a grid if you are arranging things into a table. All of the elements in a grid should be the same size.
To arrange some items vertically where the size of each row can vary, try this:
static JPanel buildPanel() {
JPanel vPanel = new JPanel();
BoxLayout layout = new BoxLayout(vPanel, BoxLayout.Y_AXIS);
vPanel.setLayout(layout);
JPanel[] rowPanels = new JPanel[5];
int counter=1;
for (int i = 0; i < rowPanels.length; i++) {
rowPanels[i] = new JPanel();
rowPanels[i].setLayout(new FlowLayout(FlowLayout.LEFT, 2, 2));
rowPanels[i].add(new JButton("button " + counter++));
rowPanels[i].add(new JButton("Your button " + counter++));
rowPanels[i].add(new JButton("Shabutton"));
vPanel.add(rowPanels[i]);
}
return vPanel;
}
public static void main(String[] args) {
JFrame gridFrame = new JFrame();
gridFrame.add(buildPanel() );
gridFrame.pack();
gridFrame.setVisible(true);
}
You can prevent the whole JFrame from resizing using gridFrame.setResizable(false);
You can prevent sapce from being added between the rows when the window is resized with a method call like this:
rowPanels[i].setMaximumSize(new Dimentsion(400,32));