How to freeze rows in JTable - java

How do I freeze rows in JTable?
This should work similar to that of Microsoft Excel in which a user can select a cell and right click and freezes/unfreezes the rows. I found A lot of answers about freezing columns but not rows.
Thanks!

The short answer is that you need to have two JScrollPanes: the top one to handle the frozen rows (with a row header to handle its frozen columns) and the bottom one for the scrollable rows (also with rowheader) - so you end up with 4 tables to manage.
Don't use scrollbars for the top scrollpane: instead add a ChangeListener to the viewport of the lower scroll pane ()
with something like the following:
public void stateChanged(ChangeEvent e) {
Point p = spMain.getViewport().getViewPosition();
p.y = 0;
spHead.getViewport().setViewPosition(p);
}
The TableHeader for the bottom pane needs to be hidden (at least when displaying the top pane):
spMain.setColumnHeaderView(bodyTable.getTableHeader());
spMain.getColumnHeader().setVisible(false);
The rest isn't exactly plain sailing but you can make your life easier if you don't have a generated column of row numbers to emulate Excel, don't allow freezing of an already frozen table (which you can't do in Excel) and don't allow sorting (at least of columns in the frozen section.
The JVM that I use has a "feature" that complicates aligning the headers in the top pane with the columns in the lower until the top tables have at least one row - consider only using the top pane once a 'freeze' is actually performed.

An easy way to do this is by overriding the table model's getValueAt() method.
You could do something like this:
Supplier<Integer> firstVisibleRowIndexSupplier = () -> {
Point p = scrollPane.getViewport()
.getViewPosition();
return table.rowAtPoint(p);
};
Pass the firstVisibleRowIndexSupplier to the table model.
In the table model:
#Override
public Object getValueAt(int row, int column) {
int firstVisibleRow = this.firstVisibleRowSupplier.get();
// Logic for returning data for the frozen rows, based on firstVisibleRow
}
The downside is that the table caches data when scrolling up vertically.
A workaround for this is to add an AdjustmentListener to the vertical scroll bar:
scrollPane.getVerticalScrollBar()
.addAdjustmentListener(e -> tableModel.refresh());
with
// TableModel
public void refresh() {
// Preserves selection
fireTableChanged(new TableModelEvent(this, //tableModel
this.firstVisibleRowSupplier.get(), //firstRow
getRowCount() - 1, //lastRow
TableModelEvent.ALL_COLUMNS, //column
TableModelEvent.UPDATE)); //changeType
}
With this workaround, there is one minor downside that I haven't found a fix for yet: When scrolling upwards with the mouse wheel, the frozen row(s) will flash briefly before the table updates. This doesn't happen as much when scrolling with the scroll bar or at all when scrolling down with either the scroll bar or mouse wheel.

Related

How to auto adjust JSplitPane?

In my Java Swing app there is a JSplitPane that splits vertically, top part is a JLabel which may change size when I click a button to update some info, bottom part is a JPanel that shows some results.
My code looks like this :
JLabel Top_Label=new JLabel();
Top_Label.setPreferredSize(new Dimension(1420,355));
JPanel Bottom_Panel=new JPanel();
Bottom_Panel.setPreferredSize(new Dimension(1420,445));
JSplitPane Results_Split_Pane=new JSplitPane(JSplitPane.VERTICAL_SPLIT,Top_Label,Bottom_Panel);
Top_Label contains an html table in it. When I click a button to update data, the Top_Label sometimes contain fewer lines of data in the html table and shows a lot of empty space.
Right now, when the Top_Label shows a small html table, it will remain its original size, and leave a lot of empty space, but after I adjust it to smaller space and click the button to update data to have a large html table, it won't enlarge the area to fit that larger html table, and yet when I manually move the split bar, it's intelligent enough to not go smaller than the html table's height, but if there is empty space, it will let me shrink the area.
How to let Results_Split_Pane auto adjust itself according to the height of Top_Label, so that when there is less content, the split bar will go higher to the height of the html table in Top_Label, when Top_Label contains more data, the Results_Split_Pane's split bar will go lower ?
A JSplitPane doesn't have an automatic adjustment feature.
I would suggest you could:
add a PropertyChangeListener to your JLabel
and listen for the text event
get the preferred size of the label
use the setDividerLocation(...) method of the JSplitPane to be the preferred height of the label.
Thanks to "camickr", I got the perfect answer for it, skip step 3 and use "-1" so that the JSplitPane will auto adjust to it's size, works great. Here is the listener :
Results_Top_Label.addPropertyChangeListener(Property_Change_Listener);
...
PropertyChangeListener propertyChangeListener=new PropertyChangeListener()
{
#Override
public void propertyChange(PropertyChangeEvent event)
{
// Out("[x] : "+event.getPropertyName());
if (event.getSource() instanceof JLabel && event.getPropertyName().equals("html"))
{
// Out(((JLabel)event.getSource()).getName()+" : Height = "+Results_Top_Label.getPreferredSize().height);
Results_Split_Pane.setDividerLocation(-1);
}
}
};

How to synchronize row header JTable scrolling with another table using keyboard arrow keys

In a component class, I have 2 JTables, one is a fixed row header table and another is a data table that carries the values for the row header. I have a JScrollPane set for the data table such that it will scroll simultaneously for both tables.
In another class, I have a JPanel which I add the component class above to display.
In the panel, the scrolling of the tables is fine when I scroll using the mouse wheel or by clicking the scroll bars. However, if I select the row header and press the down or up arrow key from the keyboard, I notice that only the row header continues to scroll, but the data table does not move. Thus, the tables rows do not align properly anymore.
In another note, I noticed that if I select the data table row instead and scroll using the up/down arrow keys, both the tables are able to scroll properly in synchronization!
Would anyone know why is this so and how I may solve the scrolling problem if the row header table was selected?
Check out Fixed Column Table
The logic there uses a ChangeListener to synchronize the scrolling of the row header with the vertical scrollbar of the scrollpane.
The basic code is:
scrollPane.getRowHeader().addChangeListener( this );
and the listener:
public void stateChanged(ChangeEvent e)
{
// Sync the scroll pane scrollbar with the row header
JViewport viewport = (JViewport) e.getSource();
scrollPane.getVerticalScrollBar().setValue(viewport.getViewPosition().y);
}

How to scroll horizontally within a single column of a resizeable JTable?

I am making a dialog for the purpose of selecting multiple file paths. My dialog consists of two panels. One for buttons such as "Add" and "Remove", and a second panel containing a JTable wrapped in a scrollPane. The table has only one column. The cells of the table are not editable directly. When a user selects a file using a JFileChooser, the full path of that file will be added to the table. Although my dialog is resizeable, I still need a horizontal scroll behavior in the event that the file path is longer than the user's screen is wide.
I have researched the combination of resizeable table and horizontal scroll bar. That is similar, but not my issue. The typical scroll behavior is that the columns are scrolled, not the contents of the columns. I need the contents of a single column to scroll horizontally.
doesn't matter whether you scroll a multiple or only a single column: the basic issue is to get the horizontal scrollBar to start with :-)
There are two screws to tweak:
- enable horizontal scrolling by setting the table's resizeMode: default is to always fit the table's size to the size of the scrollPane, that is no scrolling
- resize the column width to fit its content
In a core JTable that maps into pseudo-code like
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
// on receiving a TableModelEvent which might increase the column width
// calculate new width by measuring pref of the renderer
int newWidth = ...
// set it as pref of the column
table.getColumnModel().getColumn(0).setPreferredWidth(newWidth);
The catch is that without resizeMode, you are always responsible to sizing the column: it its width is less than the scrollPane, there's an empty region at its trailing side.
JXTable (part of SwingX project), supports an addition sizing mode which fills the available horizontal space as long as the table's prefWidts is less than parent width and shows a horizontal scrollBar if needed
table.setHorizontalScrollEnabled(true);
// on receiving a TableModelEvent which might increase the column width
// tell the table to re-evaluate
table.packColumn(0);
I selected kleopatra's answer as correct because it addresses my specific question regarding table manipulation. I am adding this answer because I ended up solving my root problem in a different manner.
I chose to use a JList to represent my file paths instead of a single column table. The only real reason that I had wanted to use the JTable was because of the appearance of the lined rows that a table has, and because of my unfamiliarity with JList. I discovered how to edit the appearance of the JList by extending the DefaultListCellRenderer. Because I now knew about editing the appearance, the JList's natural resizing and scroll behavior made it a much more natural fit to my needs.

Extending (sliding down) JTable row

I use jPanel as my cell and the table has just one column. It looks like this ( JTable: Buttons in Custom Panel in Cell ).
By default, the panel associated with the row (celll) contains a date when it was created. i want to implement an ActionListere method that will extend( slide down) the jpanel in which the action occured to see the whole jPanel data.
Hope you get what i mean.
Basically, you want to do the same thing that JList.ensureIndexIsVisible(int) but with a table.
Component has a method called "scrollRectToVisible(Rectangle aRect)" which should just about do what you need.
So, using your JTable, you can get the cellRect(int width, int height, boolean includingSpacing) and pass it off to the "scrollRectToVisible"
You're going to have to do some clever work to find out the row of the action, but I assume that you are using a table cell editor which has already provided you with this information

JTable : is it possible to disable "scroll to column"?

I've got a JTable with a model that has about 20 columns. That's more than you can fit in a single screen, so scrollbars enable the users to scroll up/down and right/left.
Now, if a user scrolls all the way to the right and clicks on a row, then that row gets selected just fine. However, if the user then use the scrollbars to scroll all the way to the left, and then press the down arrow key, the JTable automatically scrolls all the way to the right again (and selects the next row). It is as if the JTable remembers the column the user first clicked in, and when using the down arrow key the JTable just takes that column and moves down one row and scrolls back to that column.
Is there a way to disable this behaviour, so that the user remains in the selected view without JTable doing all this "magic" scrolling?
Scrolling a JTable isent connected to the cell selection.
Clicking on a cell will make the Jtable put its curose onto that cell. this means all future navigating will be from the last clicked place. Nomatter how much you scroll that last location will be the starting point of the key navigation.
But in fact the behaviour you describe is just the standard in about any gui. Take Intellij, Excel, Word, Editplus,... if you use arrow keys to navigate you always scroll back to where last clicked.
but GUi discussions aside back to your problem
i think you can make it work with
setAutoscrolls(false);
on your jtable
You could try setColumnSelectionAllowed(false), so that the user cannot select the column in the first place.
I'm preventing this behavior with this JTable override.
Note that my table does not care about cell selection, and only paints row selection (no cell selection border).
#Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
// essentially disabling cell selection, selected column index will always be 0
columnIndex = 0;
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}

Categories