JTable auto resizing to JScrollPane height and width - java

I have been reading all over the internet to try to find a solution to my problem.
Can anyone help me. How can I auto resize Jtable's height to fit parent container when there aren't enough rows for scrollbar to show. thanks
UPDATE: code that shows my problem, and how suggested solution is not helping.thanks
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
import javax.swing.JTable;
public class t extends JFrame {
private JPanel contentPane;
private JTable table;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
t frame = new t();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public t() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout(0, 0));
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
table = new JTable(10,10);
table. setFillsViewportHeight( true );
scrollPane.setViewportView(table);
}
}

How can I auto resize Jtable's height to fit parent container when there aren't enough rows for scrollbar to show.
I think you are looking for:
table. setFillsViewportHeight( true );
Edit:
but doesn't seem to work...
Add the following lines of code to your example:
contentPane.setBackground(Color.RED);
scrollPane.getViewport().setBackground(Color.BLUE);
and you will see a red border.
Then remove:
table. setFillsViewportHeight( true );
and you will see red and blue, indicating that the table height was originally increased.
If you are expecting to see empty cells painted then it won't work. The table can only paint what is in the model. If you want more rows of data painted, then you need to add more rows of data to the model. And if the height decreases you need to remove data from the model.
Of course now you need to track which is "valid" rows of data and which is "filler" rows of data. So you would need to create a custom TableModel to manage this. You would then add a ComponentListener to the table to handle the height changes in the table and then do your manipulation of the TableModel.

Related

Java fit JDialog to JTable if table size is less than a set value

I have a JDialog with two panels: a JTable and a button. I want to set JDialog so that (maxHeight= 600 pixels):
(1) If table height is less than maxLength it shows table fully with no blank space, but all rows fit in.
(2) If the height is greater than maxHeight it restricts the height to maxHeight and shows scrollbar on the side.
So far I have most of the JDialog working, but I can't figure out how to control the height of the JTable.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
public class TestGUI {
public static void main(String[] args) {
int a=10;
Object[][] data = new Object[a][2]; //Fill table with some data
for (int i = 0; i < a; i++) {data[i][0]=i; data[i][1]=i*i; }
JDialog dialog = new JDialog();
dialog.setLayout(new BorderLayout());
String[] header = {"Number", "Square"};
DefaultTableModel model = new DefaultTableModel(data, header);
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(new Dimension(450, 600));
table.setFillsViewportHeight(true);
JScrollPane js = new JScrollPane(table);
js.setVisible(true);
dialog.add(js, BorderLayout.CENTER);
JButton okButton = new JButton(new AbstractAction("OK") {
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(okButton);
dialog.add(buttonPanel, BorderLayout.SOUTH);
//dialog.setSize(new Dimension(300, 600));
dialog.setMinimumSize(new Dimension(255, 175));
dialog.setVisible(true);
}
}
Hard-coded pixel sizes are almost never a good idea. Other users won’t have the same fonts as you, and even those who do may be using different font sizes. (A 12 point font is a font whose lines of text are 12⁄72 inch high, so even if the user has the same font, the dot pitch of the user’s monitor may affect how many pixels each character uses.)
Instead of maxHeight, define a maxVisibleRows variable. Your table should use getRowHeight to compute its size based on the desired number of rows:
int visibleRows = model.getRowCount();
visibleRows = Math.max(1, visibleRows);
visibleRows = Math.min(maxVisibleRows, visibleRows);
Dimension size = table.getPreferredScrollableViewportSize();
size.height = table.getRowHeight() * visibleRows;
table.setPreferredScrollableViewportSize(size);
Now your JTable, and its enclosing JScrollPane, will have a preferred size consistent with what I think you’re seeking, which means you can (and should) use pack().
dialog.pack();
dialog.setResizable(false);
If your table may potentially have a lot of rows, you should consider avoiding any restrictions on the size of the JDialog. If there are hundreds of rows, users will not appreciate being forced to look at only eight at a time.

Java - Constraining Components below JScrollPanes with runtime change

I have a JFrame with a JScrollPane containing a JTable. Below the JTable I have placed a JButton with the SpringLayout:
l.putConstraint(SpringLayout.NORTH, but, 50, SpringLayout.SOUTH, sP);
The JButton adds a row to the JTable and updates the size of the JScrollPane. As they are constraint with each other I want the button to update his position and move downwards. However, it does not happen. As I tried the same with just a plain JTable it worked fine but I need this JScrollPane to avoid that the JTable extends itself beyond the frame.
Here is my full test code:
package tests;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SpringLayout;
import javax.swing.table.DefaultTableModel;
public class StackOverflowQRuntimeAlignment extends JFrame{
private JScrollPane sP;
private JTable tab;
private JButton but;
private DefaultTableModel dtm;
public static void main(String[] args){
StackOverflowQRuntimeAlignment frame = new StackOverflowQRuntimeAlignment("Test");
frame.setSize(new Dimension(1280, 720));
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
StackOverflowQRuntimeAlignment(String title){
super(title);
//DefaultTabelModel to create the Table
dtm = new DefaultTableModel(new String[][]{{"data1", "data2"}}, new String[]{"Column1", "Column2"});
tab = new JTable(dtm);
//set TableHeaderHeight to RowHeight
tab.getTableHeader().setPreferredSize(new Dimension(tab.getWidth(), tab.getRowHeight()));
//create ScrollPane containing the Table
sP = new JScrollPane(tab);
//set the size of the ScrollPane
sP.setPreferredSize(new Dimension(500, (dtm.getRowCount()+1)*16+3));//+1 for the Header, +3 to hide ScrollBars if they are not neccesary
sP.setMaximumSize(new Dimension(tab.getWidth(), 360));
//create the button to add an row to the table
but = new JButton("Hit me");
but.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dtm.addRow(new String[]{"Banana", "Apple"});
//updating the size of the ScrollPane
sP.setPreferredSize(new Dimension(500, (dtm.getRowCount()+1)*16+3));
sP.setSize(sP.getPreferredSize());
}
});
//constraining the Components on the GUI
SpringLayout l = new SpringLayout();
this.setLayout(l);
l.putConstraint(SpringLayout.HORIZONTAL_CENTER, sP, 0, SpringLayout.HORIZONTAL_CENTER, this.getContentPane());
l.putConstraint(SpringLayout.NORTH, sP, 50, SpringLayout.NORTH, this.getContentPane());
l.putConstraint(SpringLayout.HORIZONTAL_CENTER, but, 0, SpringLayout.HORIZONTAL_CENTER, sP);
l.putConstraint(SpringLayout.NORTH, but, 50, SpringLayout.SOUTH, sP);
//adding the Components to the GUI
this.add(sP);
this.add(but);
}
}
The point of using a JScrollPane is that you don't keep adjusting its size. As the preferred size of the components added to the scrollpane increases, scrollbars will appear on the scrollpane. So create your GUI with a default preferred size for the scroll pane and let Swing layout manager work.
Typically the scroll pane will be added to the CENTER of the BorderLayout on the frame so that as the user resizes the frame the space is allocated to the scroll pane.
However the general rules for adding (or removing) components from a visible GUI is to use:
panel.add(...);
panel.revalidate(); // to invoke the layout manager
panel.repaint(); // to paint the components

How do I remove the A on the top of the JTable inside a JScrollPane?

Why is there an A on the top of the table?
I placed a JTable inside a JScrollPane to make it scrollable.
Are there methods that I need to place?
I did not place a letter A though so I cant track.
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.BoxLayout;
import javax.swing.JTable;
import javax.swing.border.LineBorder;
import java.awt.Color;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
public class Rawr {
private JFrame frame;
private JScrollPane scrollPane;
private JTable table;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Rawr window = new Rawr();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Rawr() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.CENTER);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
scrollPane = new JScrollPane();
scrollPane.setToolTipText("");
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
panel.add(scrollPane);
table = new JTable(100, 1);
table.setBorder(new LineBorder(new Color(0, 0, 0)));
scrollPane.setViewportView(table);
}
}
How do I remove it? Thanks!
Set the column name explicitly using:
String[] colNames = new String[]{"Your Column Name"};
DefaultTableModel defaultTableModel = new DefaultTableModel(colNames, 100);
table = new JTable(defaultTableModel);
If you create a table using new JTable(100, 1) you will see the A, B and so on column headers because the constructor javadoc says:
Constructs a JTable with numRows and numColumns of empty cells using DefaultTableModel.
Since the JTable constructor does not have any information about the column headers. It can only create a DefaultTableModel that does not know any column header names.
The DefaultTableModel extends AbstractTableModel and the javadoc of AbstractTableModel.getColumnName() says
Returns a default name for the column using spreadsheet conventions: A, B, C, ... Z, AA, AB, etc. If column cannot be found, returns an empty string.
Add the following code after the creation of your table.
String[] columns = new String[]{"Column Name"};
((DefaultTableModel)table.getModel()).setColumnIdentifiers(columns);
How do I remove it?
A JTable is designed to display data with a header to describe the data in the column.
Since you only have a single column of data, if you don't want the header then don't use a JTable. Instead you can use a JList.
Read the section from the Swing tutorial on How to Use Lists for more information and examples.

JList resize after add element

I have a little problem with size of a JList:
There is a JFrame with three JLists(each in JScrollPane). You can add an item to list by click on it. After program start all JLists have the same width. But after populate any list is his width reduced and created space is added to empty list equally.
I want to stop this resizing. I hope that the code below will describe my problem better.
package test;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class Test extends JFrame {
public Test() {
GroupLayout layout = new GroupLayout(this.getContentPane());
this.setLayout(layout);
JList listA = new JList(new DefaultListModel<String>());
JList listB = new JList(new DefaultListModel<String>());
JList listC = new JList(new DefaultListModel<String>());
MouseListener listener = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
((DefaultListModel) ((JList) e.getSource()).getModel()).addElement("some text");
revalidate();
}
};
listA.addMouseListener(listener);
JScrollPane paneA = new JScrollPane(listA);
listB.addMouseListener(listener);
JScrollPane paneB = new JScrollPane(listB);
listC.addMouseListener(listener);
JScrollPane paneC = new JScrollPane(listC);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(paneA).addComponent(paneB).addComponent(paneC));
layout.setVerticalGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER).addComponent(paneA).addComponent(paneB).addComponent(paneC)));
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Test test = new Test();
test.setVisible(true);
}
});
}
}
To make your components to be of constant size you will need to use addComponent(Component component, int min, int pref, int max). So just replace your code with this
layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(paneA, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE)
.addComponent(paneB, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE)
.addComponent(paneC, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE));
instead of this
layout.setHorizontalGroup(layout.createSequentialGroup().addComponent(paneA).addComponent(paneB).addComponent(paneC));
Here I have used properties of GroupLayout and a constant width 200 to make it stable.
The JLists are likely been resized because of the size of the values been rendered are smaller than they were when they were first created. They are then be re-laid out by the layout manager to meet these new requirements.
There is a complicated amount of work going on between the JScrollPane, the JViewport, the JList and the JLists ListCellRenderer in which you don't want to get involved.
Instead, you want to provide "hints" to the JLists about how you want them to be sized.
For example...
You could use JList#setPrototypeCellValue to provide the JList with informaiton about the maximum size of the value that would be added to the list in the future. This is especially handy when you have a large number of values to add, as the JList won't need to check each one to determine the height of each row and the width required to accommodate the values.
The drawback is, you will need to know the maximum width of any of the values are likely to add...
Alternatively, you could use a LayoutManager which gives you more control over how components should be positioned and changed, such as GridBagLayout or even MigLayout
You should also check out Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
Here is one way to fix the issue.
JScrollPane paneA = new JScrollPane(listA);
paneA.setPreferredSize(new Dimension(paneA.getPreferredSize().height, 300));
listB.addMouseListener(listener);
JScrollPane paneB = new JScrollPane(listB);
paneB.setPreferredSize(new Dimension(paneB.getPreferredSize().height, 300));
listC.addMouseListener(listener);
JScrollPane paneC = new JScrollPane(listC);
paneC.setPreferredSize(new Dimension(paneC.getPreferredSize().height, 300));
I am not familiar with the GroupLayout manager. The following document might provide a way to control the layout further. http://docs.oracle.com/javase/tutorial/uiswing/layout/group.html (See Component Size and Resizability)

How to Correctly Set Minimum Sizes in Java?

For a GUI application I am creating in Java, I have the following:
A JFrame, set to have a minimum size of (300,200)
A JSplitPane, in which lies:
On the left, a JScrollPane (containing a JTree) with a minimum size of (100,0) (I only want to restrict the width to 200)
On the right, a JPanel with a minimum size of (200,0)
The sizing does not give me any issue under the following conditions:
Resizing the JSplitPane all the way to the left (to the JScrollPane's minimum size), and subsequently resize the window afterward
Just resizing the window, to a certain degree
The problem occurs when I move the JSplitPane too close to the right, whereupon resizing the window the JPanel in the right of the JSplitPane fails to adhere to the minimum width I set.
I attempted setting a maximum width on the JScrollPane, which did not seem to help at all.
Is there something involving maximum sizes I must do? Or perhaps there is a way to attach a Listener to one of the panels to force my desired behavior? Ultimately, I just want the right panel in the JSplitPane to never be less than 200px wide.
Here is an example with behavior I am experiencing:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
public class ResizeTest
{
private JFrame frame;
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
ResizeTest window = new ResizeTest();
window.frame.setVisible(true);
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
}
public ResizeTest()
{
initialize();
}
private void initialize()
{
frame = new JFrame();
frame.setMinimumSize(new Dimension(300, 200));
frame.setBounds(100,100,450,300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(0, 1, 0, 0));
JSplitPane splitPane = new JSplitPane();
frame.getContentPane().add(splitPane);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setMinimumSize(new Dimension(100, 0));
splitPane.setLeftComponent(scrollPane);
JTree tree = new JTree();
tree.setMinimumSize(new Dimension(100, 0));
scrollPane.setViewportView(tree);
JPanel panel = new JPanel();
panel.setMinimumSize(new Dimension(200, 0));
splitPane.setRightComponent(panel);
}
}
Update:
I'm afraid I don't fully understand the point trying to be made in the proposed solutions, except for that setPreferred() and setMinimum/Maximum() are better to be avoided.
My question in response to learning this is, what are my options for restricting the JSplitPane divider outside of using these methods? MadProgrammer mentioned listening for the componentResized event, but I need just a little more clarification as to why. Am I calling setDividerLocation() in response to this event?
I apologize in advance if the appended question is meant as a separate StackOverflow question entirely, I can post another and link here if necessary.
Update 2:
Is simply not regulating how the user chooses to size the window and having the right panel in a JScrollPane a viable option? This looks to me like somewhat of a standard practice.
Firstly, the method setMinimumSize is a suggestion to the LayoutManager API. A suggestion that may be ignored.
In order to be able to even come close to supporting this, you will need to use something like a ComponentListener and monitor the componentResized event.
The best solution I can think of is to use a LayoutManager that actually uses the minimum and maximum size constraints, something like GridBagLayout.
Use this on a "content" pane and place you're JSplitPane onto this (setting it's minimum and maximum size accordingly) then add the "content" pane to frame.
UPDATE
Sorry, I'm probably missing something really obvious, but I put this little test together, I hope it has some ideas that help :P
public class TestFrameSize extends JFrame {
public TestFrameSize() throws HeadlessException {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(600, 600);
setLocationRelativeTo(null);
setMinimumSize(new Dimension(250, 250));
JLabel left = new JLabel("Left");
JLabel right = new JLabel("Right");
Dimension pSize = new Dimension(100, 100);
Dimension mSize = new Dimension(25, 100);
left.setPreferredSize(pSize);
left.setMinimumSize(mSize);
right.setPreferredSize(pSize);
right.setMinimumSize(mSize);
JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, left, right);
JPanel content = new JPanel(new GridBagLayout());
content.add(pane);
setLayout(new BorderLayout());
add(content);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new TestFrameSize().setVisible(true);
}
}
In this example, the preferred size of the content of a JSplitPane is initially set to a small multiple of the component's preferred size in getPreferredSize(). Note that the JSplitPane will become no smaller than the panel's minimum size, managed by the enclosed Jlabel.

Categories

Resources