Tooltip text for each column of a JTable header - java

I am trying to display the text in each cell of the header as a tooltip when you hover over that cell.
I have found that you can set the tooltip for the entire header: table.getTableHeader().setToolTipText("asdf"); but cannot do similar for each cell such as: table.getTableHeader().getColumnModel().getColumn(0).setToolTipText("asdf");
I have looked at this question but cannot understand how to override getToolTipText when the only method in TableCellRenderer is getTableCellRendererComponent.
The only class that I've found that hass this getToolTipText is JComponent

See the section from the Swing tutorial on Specifying Tooltips For Column Headers.
I would recommend this approach because each LAF could have its own custom renderer, so extending the default renderer won't work for all LAF's.
The Windows table header is different than the MAC table header which is different than the Nimbus table header.
is it saying to create my own TableHeader?
It is overriding the code that creates the JTableHeader so you can override the getToolTipText(MouseEvent) method of the JTableHeader so you can provide your own tooltip based on the mouse location. The example code just gets the tooltip from an Array.
Would I still be able to use the text under the mouse as the tooltip?
If you want the text of the header you need to get the TableColumnModel from the JTableHeader, then get the TableColumn and then use getHeaderValue() to get the text of the column header.

I came across this since it was similar to what I needed - I wanted to put in tooltips for the column headers. The Oracle demo example linked by camickr enabled tooltips by additional code in the JTable creation. That example steered me in the right direction, and I got it working similarly, but that way of doing it was initializing a new JTable every time the table was updated. Before, I was just using myJTable.setModel() to update the table. Plus the Oracle example looked messy and was confusing for a bit there. I didn't need to extend AbstractTableModel since it didn't look like it affected tooltips at all.
So how could I get column header tooltips without making a new JTable each time and without the mess? The crucial code in the JTable initialization was overriding a method in the JTable "protected JTableHeader createDefaultTableHeader()" which of course allows for a table header (JTableHeader) with tooltips. The JTableHeader is what I really wanted to work on.
What I did is I created a new class that extended JTableHeader so that it included a tooltips String array in the constructor and a getToolTipText() method (same as the example except with out the String tip), and then I did myJTable.setTableHeader() to set it to an instance of my new class that has the tooltips String array.
(I'm posting this as an answer since it's too involved for a comment, but could be useful to others)
Here is the code in my GUI class when I update the table-
myJTable.setModel(new javax.swing.table.DefaultTableModel(
tableData,
colHeader
));//setting the new data and col headers! (no tooltips yet)
MyTableHeader headerWithTooltips = new MyTableHeader(myJTable.getColumnModel(), colHeaderTooltips);//make a new header that allows for tooltips
myJTable.setTableHeader(headerWithTooltips);//use that header in my table
And here is my MyTableHeader class-
class MyTableHeader extends JTableHeader {
String[] tooltips;
MyTableHeader(TableColumnModel columnModel, String[] columnTooltips) {
super(columnModel);//do everything a normal JTableHeader does
this.tooltips = columnTooltips;//plus extra data
}
public String getToolTipText(MouseEvent e) {
java.awt.Point p = e.getPoint();
int index = columnModel.getColumnIndexAtX(p.x);
int realIndex = columnModel.getColumn(index).getModelIndex();
return this.tooltips[realIndex];
}
}

Related

Row header in JTable with same drag/drop behaviour as column header

I'm trying to have row headers in my JTable which have similar behaviour to the column headers, especially for the drag and drop part.
Something like this: JTable Row Header Implementation
And when I drag the row header, it should have the same visual effect as when I drag the column header and the same result (row/column is moved).
So far, one way I thought of is to create my own custom BasicTableHeaderUI and implement the paint() method. Not sure if that will cause problems if other look and feels were used?
You can add D&D listener to table cells or whole table, and than pass the event to EventHandler that handles D&D events on header.

TableModel in Java: how to specify different renderers for different rows?

I'm making a custom TableModel usingAbstractTableModelfor a project, and I need to figure out a way to have a checkbox show up on some rows, but not others. I have already implemented a getColumn method, but I want to be able to have a checkbox not show up unless a certain condition in another column is reached (for example, if the object represented by a particular row is a lightbulb rather than, say, a toaster, give the user a checkbox to turn the light on or off).
I've tried using getValueAt and passing null or a string instead of a Boolean object in the hopes that maybe Swing wouldn't render the checkbox. And it doesn't, but it also throws a nasty set of ClassCastExceptions for trying to cast a String to a Boolean.
Does anyone have any ideas on how I could do something like this?
Columns in a Swing JTable are displayed using cell renderers. You should read How to Use Tables in the Java tutorials which has a section that describes how the mechanism works. This is how the core method of a custom cell renderer looks like:
public Component getTableCellRendererComponent(JTable table, Object color,
boolean isSelected, boolean hasFocus,
int row, int column) {
The task of this method is to select and prepare a Component (by setting desired colors, fonts, images...) for a specific row and column that the framework will use paint on to its Graphics context. There is a DefaultTableCellRenderer that might do the trick without too much custom code (see the tutorial). Note that this rendering mechanism is an optimization chosen by the Swing developers.
You can also learn a lot about customizing Swing components in Swing Hacks. The examples are not especially well-designed code but just show how to make creative use of the Swing API.
Good luck!
Example (see comments):
final JTable orderTable = new JTable(dataModel);
// All columns with class Boolean are renderered with MyFancyItemRenderer
orderTable.setDefaultRenderer(Boolean.class, new MyFancyItemRenderer());
// Setting the cell renderers explicitly for each column
final TableColumnModel columnModel = orderTable.getColumnModel();
final TableColumn itemCountColumn = columnModel.getColumn(ITEM_COUNT);
itemCountColumn.setCellRenderer(new MyFancyItemRenderer());
// ...
final TableColumn sumColumn = columnModel.getColumn(SUM);
sumColumn.setCellRenderer(new MyFancyPriceRenderer());

Custom cell editor can't accommodate text in Nimbus Look and Feel

I want to validate user input in a table cell, and I use the Nimbus Look and Feel.
Here is the code of a cell editor that validates integer input: WholeNumberField
It extends JTextField and adds input validation.
When I set it for the column it works fine, but it can't accommodate the text:
When I use default cell editor, it all looks fine:
How can I this editor look like the default editor?
The WholeNumberField is old code. If you really want to do custom validation then you should be using a DocumentFilter.
However, in this case, there is no need to create a custom editor. JTable already supports an editor to validate numbers. You just need to override the isCellEditable(...) method of the JTable or the TableModel to return Integer.Class and the proper renderer and editor will be used.
Edit: Just noticed my suggestion is incorrect.
you need to override getColumnClass(...) to return Integer.class so the proper renderer/editor can be used.
the isCellEditable(...) method is used to determine if you can edit a cell.
I found that putting the following to my Custom Cell Editor constructor solved the problem for me:
Border border = UIManager.getBorder("Table.cellNoFocusBorder");
if (border != null) {
setBorder(border);
}
My Editor extends JTextField.
If you get an instance of the TableCellEditor from getDefaultEditor(Object.class), it should already be a component that you can validate like in your example.

Getting a JTable with a custom table model to show up in JScrollPane

I am attempting to create my own custom TableModel for my JTable (because I would like to incorporate a row of JCheckBox's into my table.) I have the JTable in a JScrollPane as well. Before I attempted to incorporate the JCheckBox and custom AbstractTableModel, the JTable would show up fine if I used the default (Object[][], Object[]) constructor. I read in the JTable tutorial on Sun that those constructors use a default of treating all data as Strings.
I then created my custom AbstractTableModel and went from this:
JTable table = new JTable(dataArray, col);
To This:
JTable table = new JTable();
I am assuming that this would call attempt to create the JTable with the custom-made class that extends AbstractTableModel, but now nothing shows up in the JScrollPane.
I am using this incorrectly? I virtually copied the code from the Sun tutorial and only changed the names of the datafiles involved. I also placed this method in the same class. Is there a different way to make sure that your table is created with your custom Table Model? Any insight would appreciated.
JTable has several constructors that take a TableModel as a parameter. Is that what you're looking for? From the code snippet you supplied, it seems like you're calling the default constructor and expecting it to use your custom table model somehow. (Maybe there's some code missing that does this?). If you use the default constructor, JTable will internally create a DefaultTableModel instance and use that.
Edit:
Comments don't take code very well, so adding here: To get the table to use your model, you would do something like this:
MyTableModel model = new MyTableModel();
// ...initialise model if required
JTable table = new JTable(model);
As you observed, Ash is right on about passing your data model in the JTable constructor. If your model's getColumnClass() returns Boolean.class, you'll get a check box renderer by default. You might like this example that illustrates using a custom renderer and editor.
OK. After reviewing my code I realized that if I am leaving out any constructors, it will not find the link to your custom Table Model. So, if you created the class:
class MyTableModel extends AbstractTableModel {
//code here
}
You need to instantiate it in the JTable constructor like this:
JTable table = new JTable(new MyTableModel());
So you cannot just leave it blank and expect it to "find" the new AbstractTableModel class.
You need to extend AbstractTableModel, and pass this as a parameter for the constructor of your JTable. (As Marc does). In addition to the required method, you need to define this method to show the actual checkboxes:
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
This tells you JTable how to render each cell. If you dont override this, it will just be showed as a string.

Editable JTable Tutorial

Are there any good books or website that go over creating a JTable? I want to make one column editable. I would like to actually put a inherited JCheckBox component (that we created here) into one of the table columns instead of just having the table put JCheckBox in based on it being an editable boolean field.
I have the JFC Swing Tutorial Second Edition book but I just would like to know if there are other examples I could look at and learn how to deal with the tables better. The book seems to just take the java 'trail' online and put it in the book.
I am re-reading the stuff though, just curious if anyone has found something that might help out more.
To make a column editable you have to override the isCellEditable method in the TableModel. Creating a TableModel is fairly easy if you inherit AbstractTableModel and I'd recommend it for all but the most simple JTables.
However, adapting the TableModel is only part of what you need to do. To actually get a custom component in the JTable, you need to set a custom cell renderer. To use an interactive custom component, you need to set a custom cell editor. In some cases, it's enough to use slightly modificated versions of the default classes for this.
Editors
If you already have got a custom component is easily done using delegation: Create a new class implementing TableCellEditor, and return a new instance of the component in the getCellEditorComponent method. The paramaters to this method include the current value as well as the cell coordinates, a link back to the table and wether or not the cell is selected.
The TableCellEditor also has a method that is called when the user commits a change to the cell contents (where you can validate user input and adjust the model) or cancels an edit. Be sure to call the stopEditing() method on your editor if you ever programmatically abort editing, otherwise the editor component will remain on screen -- this once took me like 2 hours to debug.
Note that within a JTable editors and only editors receive events! Displaying a button can be done using a renderer. But to get a functioning button, you need to implement an editor with the correct EventListeners registered. Registering a listener on a renderer does nothing.
Renderers
Implementing a renderer is not strictly necessary for what you describe in your question, but you typically end up doing it anyway, if only for minor modifications. Renderers, unlike editors, are speed critical. The getTableCellRendererComponent of a renderer is called once for every cell in the table! The component returned by a renderer is only used to paint the cell, not for interaction, and thus can be "reused" for the next cell. In other words, you should adjust the component (e.g. using setText(...) or setFont(...) if it is a TextComponent) in the renderer, you should not instantiate a new one -- that's an easy way to cripple the performance.
Caveats
Note that for renderers and editors to work, you need to tell the JTable when to use a certain renderer/editor. There are basically two ways to do this. You can set the default cell renderer/editor for a certain type using the respective JTable methods. For this way to work, your TableModel needs to return exactly this type in the getColumnClass(...) method! The default table model will not do this for you, it always returns Object.class. I'm sure that one has stumped a lot of people.
The other way to set the editor/renderer is by explicitly setting it on the column itself, that is, by getting the TableColumn via the getTableColumn(...) method of the JTable. This is a lot more elaborate, however, it's also the only way to have two different renderers/editors for a single class. E.g. your model might have two columns of class String which are rendered in entirely different ways, maybe once using a JLabel/DefaultRenderer and the other using a JButton to access a more elaborate editor.
JTable with its custom renderers and editors is extremely versatile, but it is also a lot to take in, and there are a lot of things to do wrong. Good luck!
How to Use Tables in The Swing Tutorial is mandatory reading for anyone customising JTables. In particular, read and reread Concepts: Editors and Renderers because it typically takes a while for it to "click". The examples on custom renderers and editors are also very worthwhile.
The class you want to look into extending to create your own behavior is DefaultTableModel. That will allow you to define your own behavior. A decent tutorial can be found on sun's site.
This tutorial from the java lobby is easy to follow. The online Swing trail for JTable that you reference indicates that it has been updated. Did you scan through the whole thing for possible better (isn't newer always better) information?
If you are trying to use a simple JTable with 1 column editable and you know the column location you could always use default table model and overload the isCellEditable call.
something like this :
myTable.setModel(new DefaultTableModel(){
#Override
public boolean isCellEditable(int row, int column) {
if (column == x) {
return true;
} else
return false;
}
});
And for the check box create a renderer class
MyCheckBoxRenderer extends JCheckBox implements TableCellRenderer
Some useful classes are:
Package javax.swing.table :
TableModel - Interface for a tablemodel
AbstractTableModel - Nice class to extend for creating your own table with custom data structures
DefaultTableModel - Default table model which can deal with arrays[] and Vectors
To disable editing on any cell you need to override the isCellEditable(int row, int col) method
in your table Model, you should override "isCellEditable" and "setValueAt" functions, like below.
Column 4 is the column for editable cells.
Then when you double click the cell and type something,
setValueAt() will be called and save the value to the tableModel's DO,field col4.
public ArrayList<XXXDO> tbmData = new ArrayList<XXXDO>(); //arraylist for data in table
#Override
public boolean isCellEditable(int row, int col) {
if (col == 4) {
return true;
} else {
return false;
}
}
#Override
public void setValueAt(Object value, int row, int col) {
if ((row >= 0) && (row < this.tbmData.size()) && (col >= 0) && (col < this.colNm.length)) {
if (col == 4) {
tbmData.get(row).col4= (String) value;
}
fireTableCellUpdated(row, col);
} else {
}
}

Categories