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);
}
Related
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.
Using a JTable, my Table Model setValueAt() method moves the selection to the next row in certain cases, using setRowSelectionInterval() and setColumnSelectionInterval(). When it's called from the (default) cell editor (by user typing in the cell and hitting tab), the code works: the desired next cell is selected (the first one on the next row).
However, if the user uses Return rather than Tab to commit the edit, the selection doesn't happen; instead the cell below is selected. That's fine with me.
I also have a JButton to clear a row. The button's action function calls the model's setValueAt() function for the desired cells. Unfortunately, the setRowSelectionInterval() and setColumnSelectionInterval() methods have no apparent effect; instead, no cells are selected.
I've tried table.requestFocusInWindow() and table.getParent().requestFocusInWindow(), as well as table.changeSelection(row, 0, false, false), all to no apparent effect.
Is there anything basic I'm missing here, before I go to the trouble of building the SSCCE?
In case it matters, here's the container hierarchy:
parent JPanel
button rows JPanel
button row 1 JPanel
button row 2 JPanel
table JScrollpane
JTable
The button in question is in button row 1.
Thanks!
Maybe you can use the Table Cell Listener to listen for edits to the table. It listens for actual changes done by the JTable editor.
Then in the supplied Action you can select the appropriate row. You may need to wrap the Action code in a SwingUtilities.invokeLater(...) to make sure the code executes after the table is completely finished editing.
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);
}
If required I'll post an SSCCE.
I have a JTable with JComboBoxes # First and Second Columns.
Upon a selection in the First Column JComboBox of a row, I want to update this row's Second Column JComboBox. (Chained Selections - I know how to do this with plain JComboBoxes but things go wrong with JTable)
I've tried getValueAt(int row, int col) method to change the CellEditor and dummy set the value as an empty string (like no selection), but it doesn't work properly. Doesn't properly update JComboBox and doesn't allow for selection and some other weird things.
Also, tried ItemListener for the First Column JComboBox but I can't find a way to properly update the Second Column JComboBox. I tried changing the CellEditor of the selected row, but it seems to be messing up with other rows as well, it's like more it remembers a previously selected row or something similar.
What is the proper way of doing chained selects in a JTable? I've been messing with this for almost a week..
In your case you should work with cell editors (your JCombobox). From editor you can get value from JCombobox and set this value to another JCombobox.
In my opinion you can 1) get selected cell; 2) from cell you can get cell editor; 3) from editor (if editor is JCombobox you can get text field component and from this component you can get value, jCombobox must be editable in this case) you can get value.
I noticed that I can you arrows to move row selection of my JTable object only when I press tab key. Is it possible to use arrows after row selection by mouse-click (instead of using TAB)?
In order for the arrow keys to change row selection, the JTable must have focus. Pressing the tab key changes focus to the next (or first) "focussable" Component on the page which is likely a SubComponent in the JTable.
To get it to focus automatically when it becomes visible, add a ComponentListener with the an componentShown(...) method implemented to call the JTable's requestFocusInWindow() method.
Is it possible to use arrows after row selection by mouse-click (instead of using TAB)
Yes; if you click the mouse on a row, that should also focus the row, allowing you to use the arrow keys as well.
Updated: corrected method used to get input focus, with thanks to camickr (see comments)