Is there any way to disable selection of multiple columns for a Swing JTable? I've disabled selection all together in the "Tid" column by overriding the selection intervals of the selection model:
myTable.getColumnModel().setSelectionModel(new DefaultListSelectionModel() {
private boolean isSelectable(int index0, int index1) {
return index1 != 0;
}
#Override
public void setSelectionInterval(int index0, int index1) {
if(isSelectable(index0, index1)) {
super.setSelectionInterval(index0, index1);
}
}
#Override
public void addSelectionInterval(int index0, int index1) {
if(isSelectable(index0, index1)) {
super.addSelectionInterval(index0, index1);
}
}
});
And my guess is that one can also disallow the selection of multiple columns by overriding methods in the selection model. But I can't really figure out how to accomplish that.
Allowed selection
Disallowed selection
First get the TableColumnModel from the JTable
TableColumnModel columnModel = table.getColumnModel();
Next, get the LstSeletionModel for the TableColumnModel
ListSelectionModel selectionModel = columnModel.getSelectionModel();
With this, you could set the selectionMode that the model will use, for example
selectionModel.setSelectionModel(ListSelectionModel.SINGLE_SELECTION)
See the JavaDocs for ListSelectionModel and TableColumnModel for more details
Runnable example....
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel(0, 10);
for (int row = 0; row < 10; row++) {
String[] data = new String[10];
for (int col = 0; col < 10; col++) {
data[col] = row + "x" + col;
}
model.addRow(data);
}
JTable table = new JTable(model);
table.setColumnSelectionAllowed(true);
table.setRowSelectionAllowed(true);
table.getColumnModel().getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
add(new JScrollPane(table));
}
}
}
Actually, it was a simple enough addition to my already existing overrides that was needed.
#Override
public void setSelectionInterval(int index0, int index1) {
if (isSelectable(index0, index1)) {
if (index0==index1) { //The if condition needed.
super.setSelectionInterval(index0, index1);
}
}
}
I realised upon reviewing the JavaDoc and the DefaultListSelectionModel that the index0 and index1 were just what I was looking for - the column span. So by doing the call to the superclass if and only if the two column indices are equal, selection of multiple columns is not possible.
Related
The below code is part of my button action.Jtable contain last row is checkbox.
When i click save button the selected row must delete from table row...!!!'
Action performed code
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnSave){
for (int i = 0; i < retunTable.getRowCount(); i++) {
Boolean chked = Boolean.valueOf(retunTable.getValueAt(i, 4)
.toString());
String dataCol1 = retunTable.getValueAt(i, 1).toString();
if (chked) {
JOptionPane.showMessageDialog(null, dataCol1);
colVaules.add(dataCol1);
returnBook();
DefaultTableModel dm=(DefaultTableModel) retunTable.getModel();
}
}
}
}
Try this out. You're class should already have overridden the getColumnClass() of the model, so no need for the toString() thing your trying to do. The getValueAt() should return a cast-able Boolean object. Also if you are going to loop and remove rows dynamically in the loop, you need to take into account that the model's row count will decrease with each removal of a row, so will also need to i-- every time a row is removed. See example below.
import java.awt.BorderLayout;
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.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class TestCheckedTable {
public TestCheckedTable() {
String[] cols = {"col 1", "col 2", "col 3"};
Object[][] data = new Object[15][];
for (int i = 0; i < data.length; i++) {
data[i] = new Object[]{"Hello", "World", false};
}
final DefaultTableModel model = new DefaultTableModel(data, cols) {
#Override
public Class<?> getColumnClass(int col) {
return col == 2 ? Boolean.class : String.class;
}
};
JTable table = new JTable(model);
JButton button = new JButton("Delete Checked Rows");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < model.getRowCount(); i++) {
Boolean checked = (Boolean) model.getValueAt(i, 2);
if (checked) {
model.removeRow(i);
i--;
}
}
}
});
JFrame frame = new JFrame("test");
frame.add(new JScrollPane(table));
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestCheckedTable();
}
});
}
}
I have following code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;
import javax.swing.table.DefaultTableModel;
public class NewClass1 extends JFrame {
private JTable table;
private JScrollPane scrollPane;
private DefaultTableModel defaultTableModel;
public NewClass1() {
setLocationByPlatform(true);
setLayout(new BorderLayout());
setPreferredSize(new Dimension(600, 400));
setTitle("Table Issues");
setDefaultCloseOperation(EXIT_ON_CLOSE);
createTableModel();
table = new JTable(defaultTableModel);
scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane, BorderLayout.CENTER);
pack();
}
private void createTableModel() {
Vector cols = new Vector();
cols.add("A");
Vector rows = new Vector();
for (int i = 0; i < 50; i++) {
Vector row = new Vector();
row.add((i + 1) + "");
rows.add(row);
}
defaultTableModel = new DefaultTableModel(rows, cols) {
Class[] types = new Class[]{
String.class
};
#Override
public Class getColumnClass(int columnIndex) {
return types[columnIndex];
}
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(new NimbusLookAndFeel());
} catch (Exception e) {
}
final NewClass1 nc = new NewClass1();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
nc.setVisible(true);
}
});
while (true) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
int row = (int) (Math.random() * 50);
int move = (int) (Math.random() * 50);
nc.defaultTableModel.moveRow(row, row, move);
}
});
try{
Thread.sleep(1000);
}catch(Exception e){
}
}
}
}
Please run the above code and select row.
My problem is with row movement, row selection is not moving. It is staying at fixed position. Suppose I selected row with column value 25, selected row must be of column value 25 after row movements.
Please help me on this.
My real problem is, user will select row and clicks menu to perform action, meanwhile other threads may have moved rows, and performed action will be on other row than actual one.
The easiest way is to remember the selected row somewhere outside of the ListSelectionModel and adjust the selection whenever the TableModel changes. For example you could do this:
public class NewClass1 extends JFrame {
private JTable table;
private DefaultTableModel defaultTableModel;
private JScrollPane scrollPane;
private class SelectionHelper implements ListSelectionListener, TableModelListener {
private Object selectedRow;
#Override
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) return;
int selectedIndex = table.getSelectedRow();
if (selectedIndex >= 0) {
selectedRow = defaultTableModel.getDataVector().get(selectedIndex);
} else {
selectedRow = null;
}
}
#Override
public void tableChanged(TableModelEvent event) {
if (selectedRow == null) return;
int selectedIndex = defaultTableModel.getDataVector().indexOf(selectedRow);
table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
}
}
public NewClass1() {
// ...
createTableModel();
table = new JTable(defaultTableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
SelectionHelper helper = new SelectionHelper();
table.getModel().addTableModelListener(helper);
table.getSelectionModel().addListSelectionListener(helper);
// ...
}
// ...
}
Note however, that you should adjust this code for production use, for example in regards to thread safety or portability (using the table and defaultTableModel attributes in the inner class is bad style).
I'm removing all rows of a JTable using this:
myTableModel.getDataVector().removeAllElements();
myTableModel.setRowCount(0);
But after deletion its footprint remains! please check this Screenshot:
This only happens when I delete all rows, and if there be even one row there will be bo problem!
Why this happens? how can I fix it?
Thanks
Using a DefaultTableModel, the only option you have is to use the functionality provided by the model, removeRow
removeRow will fire the appropriate events required to tell the JTable that it needs to update itself. The table has been optimised in such away as to improve the painting process
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class TestTable {
public static void main(String[] args) {
new TestTable();
}
public TestTable() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
DefaultTableModel model = new DefaultTableModel();
JTable table = new JTable(model);
for (int index = 0; index < 10; index++) {
model.addColumn(index);
}
for (int row = 0; row < 1000; row++) {
String[] values = new String[10];
for (int col = 0; col < 10; col++) {
values[col] = row + "x" + col;
}
model.addRow(values);
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.add(new JButton(new DeleteRowsFromDefaultModel(model)), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DeleteRowsFromDefaultModel extends AbstractAction {
private final DefaultTableModel model;
public DeleteRowsFromDefaultModel(DefaultTableModel model) {
this.model = model;
putValue(NAME, "Delete All");
}
#Override
public void actionPerformed(ActionEvent e) {
model.setRowCount(0);
}
}
}
If you're really worried about performance, you will need to create your own model and create your own clear method that fires the appropriate events.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
public class TestTable1 {
public static void main(String[] args) {
new TestTable1();
}
public TestTable1() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MyTabelModel model = new MyTabelModel();
JTable table = new JTable(model);
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.add(new JButton(new DeleteRowsFromMyModel(model)), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DeleteRowsFromMyModel extends AbstractAction {
private final MyTabelModel model;
public DeleteRowsFromMyModel(MyTabelModel model) {
this.model = model;
putValue(NAME, "Delete All");
}
#Override
public void actionPerformed(ActionEvent e) {
model.clear();
}
}
public class MyTabelModel extends AbstractTableModel {
private List<String[]> rows;
public MyTabelModel() {
rows = new ArrayList<>(1000);
for (int row = 0; row < 1000; row++) {
String[] values = new String[10];
for (int col = 0; col < 10; col++) {
values[col] = row + "x" + col;
}
rows.add(values);
}
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return 10;
}
#Override
public String getColumnName(int column) {
return String.valueOf(column);
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
String[] row = rows.get(rowIndex);
return row[columnIndex];
}
public void clear() {
int old = getRowCount();
if (old > 0) {
rows.clear();
fireTableRowsDeleted(0, old - 1);
}
}
}
}
I have two JTables aligned horizontally inside JScrollBar. When I select a cell in the first JTable I would like the corresponding cell on the other JTable to be aligned.
What I'm doing is that I use, each time a cell in the first JTable is selected, the JTable.getCellRect() method to get the the y position of each cell and then use JTable.scrollRectToVisible(firstJTableCell). The cell in the second JTable becomes then visible, but I would like to know how to align it with the selected cell in the first JTable.
I have a way to know which cell to select in the second JTable when a cell is selected in the first JTable. What I would like to know, is how to align two JTables cell programatically.
There are probably better ways to achieve the same result, essentally though, you can just use an AdjustmentListener and a ListSelectionListener to monitor changes to the JScrollPanes and JTables and sync those results back to the other table (based on the source of the original event)
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class SyncedTables {
public static void main(String[] args) {
new SyncedTables();
}
public SyncedTables() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable left;
private JTable right;
public TestPane() {
setLayout(new GridLayout(1, 2));
left = new JTable(createTableModel("L"));
right = new JTable(createTableModel("R"));
left.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
right.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane leftScrollPane = new JScrollPane(left);
JScrollPane rightScrollPane = new JScrollPane(right);
AdjustmentHandler ah = new AdjustmentHandler(leftScrollPane, rightScrollPane);
leftScrollPane.getVerticalScrollBar().addAdjustmentListener(ah);
leftScrollPane.getHorizontalScrollBar().addAdjustmentListener(ah);
rightScrollPane.getVerticalScrollBar().addAdjustmentListener(ah);
rightScrollPane.getHorizontalScrollBar().addAdjustmentListener(ah);
SelectionHandler sh = new SelectionHandler(left, right);
left.getSelectionModel().addListSelectionListener(sh);
right.getSelectionModel().addListSelectionListener(sh);
add(leftScrollPane);
add(rightScrollPane);
}
protected TableModel createTableModel(String prefix) {
DefaultTableModel model = new DefaultTableModel(0, 50);
for (int row = 0; row < 100; row++) {
Object[] rowData = new Object[50];
for (int col = 0; col < 50; col++) {
rowData[col] = prefix + "-" + row + "x" + col;
}
model.addRow(rowData);
}
return model;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public class SelectionHandler implements ListSelectionListener {
private JTable left;
private JTable right;
private boolean ignore;
public SelectionHandler(JTable left, JTable right) {
this.left = left;
this.right = right;
}
#Override
public void valueChanged(ListSelectionEvent e) {
if (!ignore) {
ignore = true;
if (e.getSource() == left.getSelectionModel()) {
sync(left, right);
} else if (e.getSource() == right.getSelectionModel()) {
sync(left, right);
}
ignore = false;
}
}
protected void sync(JTable master, JTable slave) {
slave.clearSelection();
int[] selectedRows = master.getSelectedRows();
for (int row : selectedRows) {
slave.addRowSelectionInterval(row, row);
}
int[] selectedColumns = master.getSelectedColumns();
for (int cols : selectedColumns) {
slave.addColumnSelectionInterval(cols, cols);
}
}
}
public class AdjustmentHandler implements AdjustmentListener {
private JScrollPane left;
private JScrollPane right;
private boolean ignore;
public AdjustmentHandler(JScrollPane left, JScrollPane right) {
this.left = left;
this.right = right;
}
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (!ignore) {
ignore = true;
Container parent = ((Component) e.getSource()).getParent();
if (parent == left) {
sync(left, right);
} else if (parent == right) {
sync(right, left);
}
ignore = false;
}
}
protected void sync(JScrollPane master, JScrollPane slave) {
slave.getHorizontalScrollBar().setValue(master.getHorizontalScrollBar().getValue());
slave.getVerticalScrollBar().setValue(master.getVerticalScrollBar().getValue());
}
}
}
I am running into a problem that has been discussed on here before: getting a JScrollPane containing a JTable to display the horizontal scrollbar as I desire. HERE is a post I tried following, but for some reason it does not seem to work with my situation (I think it is because I've set one of my columns to have a fixed width.
Here is a SSCCE:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
#SuppressWarnings("serial")
public class TableScrollTest extends JFrame
{
public TableScrollTest() {
DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0);
model.addRow(new Object[]{"short", "blah"});
model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});
JTable table = new JTable(model) {
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
};
table.getColumn("key").setPreferredWidth(60);
table.getColumn("key").setMinWidth(60);
table.getColumn("key").setMaxWidth(60);
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
JScrollPane scrollPane = new JScrollPane( table );
getContentPane().add( scrollPane );
}
public static void main(String[] args) {
TableScrollTest frame = new TableScrollTest();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setSize(200, 200);
frame.setResizable(false);
frame.setVisible(true);
}
}
In short, I have a two column table for displaying key/value pairs. The container holding the table is of fixed width, and the first column of the table is also of fixed width (since I know how long all the key names will be). The second column will contain values of varying string length. A horizontal scrollbar should only appear when there are values present which are too long to fit in the width allotted for the column.
Because the second value above has such a long length, the scrollbar should be visible. However, it's not, and everything I have tried has only succeeded in getting it visible always, which is not what I want... I only want it visible if "long" values are present. It seems like the getScrollableTracksViewportWidth() method being overridden in the table constructor checks what the preferred width of the table is... so somehow I need to direct the table to prefer a larger width based on the contents of that second column only... but I'm stumped.
Any ideas?
This is a hackey solution
Basically, what it does is calculates the "preferred" width of all the columns based on the values from all the rows.
It takes into consideration changes to the model as well as changes to the parent container.
Once it's done, it checks to see if the "preferred" width is greater or less than the available space and sets the trackViewportWidth variable accordingly.
You can add in checks for fixed columns (I've not bothered) which would make the process "slightly" faster, but this is going to suffer as you add more columns and rows to the table, as each update is going to require a walk of the entire model.
You could put some kind of cache in, but right about then, I'd be considering fixed width columns ;)
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class TableScrollTest extends JFrame {
public TableScrollTest() {
DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"}, 0);
model.addRow(new Object[]{"short", "blah"});
model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});
JTable table = new JTable(model) {
private boolean trackViewportWidth = false;
private boolean inited = false;
private boolean ignoreUpdates = false;
#Override
protected void initializeLocalVars() {
super.initializeLocalVars();
inited = true;
updateColumnWidth();
}
#Override
public void addNotify() {
super.addNotify();
updateColumnWidth();
getParent().addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
invalidate();
}
});
}
#Override
public void doLayout() {
super.doLayout();
if (!ignoreUpdates) {
updateColumnWidth();
}
ignoreUpdates = false;
}
protected void updateColumnWidth() {
if (getParent() != null) {
int width = 0;
for (int col = 0; col < getColumnCount(); col++) {
int colWidth = 0;
for (int row = 0; row < getRowCount(); row++) {
int prefWidth = getCellRenderer(row, col).
getTableCellRendererComponent(this, getValueAt(row, col), false, false, row, col).
getPreferredSize().width;
colWidth = Math.max(colWidth, prefWidth + getIntercellSpacing().width);
}
TableColumn tc = getColumnModel().getColumn(convertColumnIndexToModel(col));
tc.setPreferredWidth(colWidth);
width += colWidth;
}
Container parent = getParent();
if (parent instanceof JViewport) {
parent = parent.getParent();
}
trackViewportWidth = width < parent.getWidth();
}
}
#Override
public void tableChanged(TableModelEvent e) {
super.tableChanged(e);
if (inited) {
updateColumnWidth();
}
}
public boolean getScrollableTracksViewportWidth() {
return trackViewportWidth;
}
#Override
protected TableColumnModel createDefaultColumnModel() {
TableColumnModel model = super.createDefaultColumnModel();
model.addColumnModelListener(new TableColumnModelListener() {
#Override
public void columnAdded(TableColumnModelEvent e) {
}
#Override
public void columnRemoved(TableColumnModelEvent e) {
}
#Override
public void columnMoved(TableColumnModelEvent e) {
if (!ignoreUpdates) {
ignoreUpdates = true;
updateColumnWidth();
}
}
#Override
public void columnMarginChanged(ChangeEvent e) {
if (!ignoreUpdates) {
ignoreUpdates = true;
updateColumnWidth();
}
}
#Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
});
return model;
}
};
table.getColumn("key").setPreferredWidth(60);
// table.getColumn("key").setMinWidth(60);
// table.getColumn("key").setMaxWidth(60);
// table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
TableScrollTest frame = new TableScrollTest();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setSize(200, 200);
frame.setResizable(true);
frame.setVisible(true);
}
});
}
}
It seems then that my goal is to force the cell renderer to automatically make cells fit long strings
This isn't the job of the renderer. You must manually set the width of the columns.
See Table Column Adjuster for one way to do this.
I won't necessarily accept this answer if something smarter is posted, but here is a solution I figured out on my own based on comments posted so far and THIS post. The only catch is that the width that I was coming up with was always ~1 pixel too short (in some look-and-feel's it was ok), so I added the line near the end width += 5 which seems to make it work ok, but it feels hacky to me. Would welcome any comments on this approach.
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
#SuppressWarnings("serial")
public class TableScrollTest extends JFrame {
public TableScrollTest() {
DefaultTableModel model = new DefaultTableModel(new Object[]{"key", "value"},0);
model.addRow(new Object[]{"short", "blah"});
model.addRow(new Object[]{"long", "blah blah blah blah blah blah blah"});
JTable table = new JTable(model);
table.getColumn("key").setPreferredWidth(60);
table.getColumn("key").setMinWidth(60);
table.getColumn("key").setMaxWidth(60);
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
int width = 0;
for (int row = 0; row < table.getRowCount(); row++) {
TableCellRenderer renderer = table.getCellRenderer(row, 1);
Component comp = table.prepareRenderer(renderer, row, 1);
width = Math.max (comp.getPreferredSize().width, width);
}
width += 5;
table.getColumn("value").setPreferredWidth(width);
table.getColumn("value").setMinWidth(width);
table.getColumn("value").setMaxWidth(width);
JScrollPane scrollPane = new JScrollPane( table );
getContentPane().add( scrollPane );
}
public static void main(String[] args) {
TableScrollTest frame = new TableScrollTest();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setSize(200, 200);
frame.setResizable(false);
frame.setVisible(true);
}
}