I'm trying to place a JComboBox inside a certain column of a JTable.
I have this code, and it is working:
model = new DefaultTableModel();
JComboBox<String> optionComboCell = new JComboBox<String>();
optionComboCell.addItem("Option 1");
optionComboCell.addItem("Option 2");
optionComboCell.setSelectedIndex(1);
table = new JTable(model);
// Adding here all the columns, removed for clarity
model.addColumn("Options");
TableColumn optionsColumn = table.getColumn("Options");
optionsColumn.setCellEditor(new DefaultCellEditor(optionComboCell));
My problem with this, is that it doesn't show as JComboBox until a cell in that column is selected.
When the JFrame is loaded, the whole table looks the same, as if all the cells where only text.
When clicked, it shows the combo box's arrow and options, but again when deselected, it looks like a regular cell.
Any way to get around that?
Yes, use a JComboBox to render your cells:
import java.awt.Component;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class Test4 {
private static class ComboBoxCellRenderer extends JComboBox implements TableCellRenderer {
public ComboBoxCellRenderer(int column) {
for (int i = 0; i < 10; i++) {
addItem("Cell (" + i + "," + column + ")");
}
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setSelectedItem(value);
return this;
}
}
protected void initUI() {
JFrame frame = new JFrame("test");
frame.add(getTable());
frame.pack();
frame.setVisible(true);
}
private Component getTable() {
Vector<Vector<String>> data = new Vector<Vector<String>>();
for (int i = 0; i < 10; i++) {
Vector<String> row = new Vector<String>();
for (int j = 0; j < 3; j++) {
row.add("Cell (" + i + "," + j + ")");
}
data.add(row);
}
Vector<String> columns = new Vector<String>();
columns.add("Column 1");
columns.add("Column 2");
columns.add("Column 3");
DefaultTableModel model = new DefaultTableModel(data, columns);
JTable table = new JTable(model);
table.setRowHeight(20);
int i = 0;
Enumeration<TableColumn> c = table.getColumnModel().getColumns();
while (c.hasMoreElements()) {
TableColumn column = c.nextElement();
column.setCellRenderer(new ComboBoxCellRenderer(i));
i++;
}
JScrollPane scroll = new JScrollPane(table);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
return scroll;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test4().initUI();
}
});
}
}
You will need to define your own renderer for displaying components on your table, since CellEditors are only needed for editing the value within a tablecell (which is why it only reacts, when you click on a cell).
Maybe have a look at Java Tutorials to learn more about the concept of renderers and editors for JTables.
Try to set the cell renderer as well.
Related
I can select and set focus to cells in a JTable by clicking it, now I want to change the value on the focused cell. In order to change the value of it, I have to double click! Is there any way to clear/change the value of that focused cell (by single-clicking)?
I have tried jTable1.setValueAt("", row, column);, this clears the value in the background(It's not updated in the GUI/Same old value appears in the cell).
Table structure:
jTable1.addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(final MouseEvent e) {
if (e.getClickCount()==1){
final JTable jTable=(JTable)e.getSource();
final int row = jTable.getSelectedRow();
final int column = jTable.getSelectedColumn();
jTable1.editCellAt(row,column);
jTable1.getEditorComponent().requestFocus();
final Double valueInCell = (Double)jTable.getValueAt(row, column);
System.out.println(valueInCell);
}
}
});
If all you desire is to clear or change a JTable cell that holds Strings, why not simply call JTable's setValueAt(Object o, int row, int column) method? To clear, pass in "", to set to something different, then pass in a different String.
For example, my minimal reproducible example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Vector;
import java.util.Random;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
#SuppressWarnings("serial")
public class TableFoo extends JPanel {
private static final Vector<String> COLUMN_NAME_VECTOR = new Vector<String>(
Arrays.asList(new String[] { "A", "B", "C" }));
private static final int COLUMN_COUNT = COLUMN_NAME_VECTOR.size();
private JTable table = new JTable();
public TableFoo() {
table.setFillsViewportHeight(true);
fillTableData();
MyMouse myMouse = new MyMouse();
table.addMouseListener(myMouse);
setLayout(new BorderLayout());
add(new JScrollPane(table), BorderLayout.CENTER);
}
class MyMouse extends MouseAdapter {
#Override
public void mousePressed(final MouseEvent e) {
final int row = table.getSelectedRow();
final int column = table.getSelectedColumn();
table.setValueAt("", row, column);
}
}
public void fillTableData() {
Vector<Vector<String>> matrix = new Vector<Vector<String>>();
int rowCount = 8;
for (int i = 0; i < rowCount ; i++) {
Vector<String> row = new Vector<String>();
for (int j = 0; j < COLUMN_COUNT; j++) {
String rowText = String.format("row %d col %d", i, j);
row.add(rowText );
}
matrix.add(row);
}
DefaultTableModel model = new DefaultTableModel(matrix,
COLUMN_NAME_VECTOR);
table.setModel(model);
}
private static void createAndShowGui() {
TableFoo mainPanel = new TableFoo();
JFrame frame = new JFrame("TableFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
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 am newbie on java-swing. I am trying to add icon in table cell. but when i add ImageIcon in cell then it's showing only path instead of icon.
Here is my code.
public void createGUI(ArrayList<String> params, String type) {
try {
DefaultTableModel model = new DefaultTableModel();
model.addColumn("ParameterName");
model.addColumn("ParameterType");
model.addColumn("Operation");
for (int i = 0; i < params.size() - 4; i++) {
String param_name = params.get(i).toString().substring(0, params.get(i).toString().indexOf("["));
String param_type = params.get(i).toString().substring(params.get(i).toString().indexOf("[") + 1, params.get(i).toString().indexOf("]"));
//URL url = ClassLoader.getSystemClassLoader().getResource("");
ImageIcon image = new ImageIcon("/com/soastreamer/resources/delete_idle.png");
// JLabel label = new JLabel(image);
model.addRow(new Object[]{param_name, param_type.toUpperCase(),image});
}
Action delete = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JTable table = (JTable) e.getSource();
int modelRow = Integer.valueOf(e.getActionCommand());
((DefaultTableModel) table.getModel()).removeRow(modelRow);
}
};
Here is image for clear understanding.
Please give me hint or any reference.
Thank you.
The problem lies with your TableModel, you have to tell the table that it has to render an image in that column overriding the getColumnClass(int column) method of the model.
Look at this answer by camickr.
UPDATE
Minimal example of a JTable with an ImageIcon using DefaultTableModel's renderer to paint it. I borrowed the updateRowHeights() code from this answer again by camickr, without it the icon was bigger than the row and wasn't fully displayed.
The important thing here is that now when the renderer calls getColumnClass(1), it gets ImageIcon.class so the code to render icons will be executed. By default this method would return Object.class and the renderer would ignore the fact that it's an icon.
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
public class ImageIconTable
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ImageIconTable().initGUI();
}
});
}
public void initGUI()
{
JFrame frame = new JFrame();
DefaultTableModel tableModel = new DefaultTableModel()
{
#Override
public Class getColumnClass(int column)
{
if (column == 1) return ImageIcon.class;
return Object.class;
}
};
tableModel.addColumn("Row 1");
tableModel.addColumn("Icons Row");
tableModel.addRow(new Object[]{"This cell is an Object", new ImageIcon("icon.jpg")});
_table = new JTable(tableModel);
updateRowHeights();
frame.add(new JScrollPane(_table), BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void updateRowHeights()
{
try
{
for (int row = 0; row < _table.getRowCount(); row++)
{
int rowHeight = _table.getRowHeight();
for (int column = 0; column < _table.getColumnCount(); column++)
{
Component comp = _table.prepareRenderer(_table.getCellRenderer(row, column), row, column);
rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
}
_table.setRowHeight(row, rowHeight);
}
}
catch(ClassCastException e) {}
}
private JTable _table;
}
It looks like this:
First, I suggest you to use ImageIo.read() and use the BufferedImage returned as argument for your ImageIcon object.
Second, use the Class.getResource() facility
YourClass.class.getResource("/com/soastreamer/resources/delete_idle.png");
Then, everything should work.
I have a JTable with a custom ListSelectionModel, which contains a custom selection mode. This selection mode ensures that a selection is only made in one row, but with multiple cells. When a user clicks selects some cells in the table, the selected cells are all in the same row as the first selected cell.
Now I want to create a small message above the upper-right corner of the leading selection cell, which displays some data, i.e. the count of the selected cells. The message should be moved when the leading selection is changed.
But how do I do that? It is not a Tooltip as it is intended to be shown when user clicks into the table and selects cells and not by hovering over it.
Any suggestions?
best regards,
htz
Alternatively (not very pretty but works quite well), you can take advantage of the fact that JTable is a JComponent like all others and therefore you can add child components to it. All you have to do, is make sure to size and locate properly your component (this is only because JTable uses a null-layout).
Here is a small demo with a JLabel that displays the number of selected items. The label is automatically positioned on the first visible row, unless it is the current lead selection, in which case, the label is moved to the second visible row:
import java.awt.BorderLayout;
import java.awt.Point;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
public class TestTable {
protected void initUI() {
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
Vector<String> colNames = new Vector<String>();
for (int i = 0; i < 5; i++) {
colNames.add("Col-" + (i + 1));
}
for (int i = 0; i < 200; i++) {
Vector<Object> row = new Vector<Object>();
for (int j = 0; j < 5; j++) {
row.add("Cell " + (i + 1) + "-" + (j + 1));
}
data.add(row);
}
table = new JTable(data, colNames);
someText = new JLabel();
someText.setOpaque(true);
table.add(someText);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
int count = table.getSelectedRowCount();
someText.setText("You currently have selected " + count + " item" + (count > 1 ? "s" : ""));
layoutLabel();
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
scrollpane = new JScrollPane(table);
scrollpane.getViewport().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
layoutLabel();
}
});
frame.add(scrollpane, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
private JLabel someText;
private JTable table;
private JScrollPane scrollpane;
private void layoutLabel() {
someText.setSize(someText.getPreferredSize());
Point location = scrollpane.getViewport().getViewRect().getLocation();
int leadSelectionIndex = table.getSelectionModel().getLeadSelectionIndex();
if (leadSelectionIndex > -1) {
if (table.rowAtPoint(location) == leadSelectionIndex) {
location.y += table.getRowHeight(leadSelectionIndex);
}
}
someText.setLocation(location);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestTable().initUI();
}
});
}
}
you can to set Locations for ToolTip with the similair effect like as in MsExcell,
it is not a Tooltip as it is intended to be shown when user clicks into the table and selects cells and not by hovering over it.
you can to use JPopup / JWindow instead of ToolTip, for non_editable contens
JPopup / JWindow by default can't contains editable JComponent (JTextComponents)
for user input you can to use undecorated JDialod only
Yet another option is to implement a per-component glassPane (one of the many roles of JLayer for 1.7, JXLayer for 1.6) like shown below. Note that I didn't try to prettify the location (as #Guillaume did). Also, you'll have to modify the Rob's DragLayout a bit to guarantee that the box is shown inside the table area.
public static class ToolTipUI extends LayerUI<JTable> {
private JLayer<JTable> layer;
private JToolTip toolTip;
#Override
public void installUI(JComponent c) {
super.installUI(c);
this.layer = (JLayer) c;
installGlassPane();
installListeners();
}
private void installListeners() {
ListSelectionListener l = new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) return;
updateToolTip();
}
};
getTable().getColumnModel().getSelectionModel().addListSelectionListener(l);
getTable().getSelectionModel().addListSelectionListener(l);
}
private void updateToolTip() {
int[] selectedColumns = getTable().getColumnModel().getSelectedColumns();
int selectedRow = getTable().getSelectedRow();
if (selectedRow < 0 || selectedColumns.length == 0) {
setToolTipText("");
} else {
String text = "selected cells: ";
for (int i = 0; i < selectedColumns.length; i++) {
text += " " + selectedColumns[i];
}
setToolTipText(text);
}
}
private void setToolTipText(String string) {
toolTip.setTipText(string);
Rectangle cellBounds = getTable().getCellRect(getTable().getSelectedRow(), 0, false);
toolTip.setLocation(cellBounds.getLocation());
doLayout(layer);
}
#Override
public void doLayout(JLayer<? extends JTable> l) {
super.doLayout(l);
l.getGlassPane().doLayout();
}
private JTable getTable() {
return layer.getView();
}
private void installGlassPane() {
toolTip = ((JComponent) layer.getView()).createToolTip();
layer.getGlassPane().setBorder(BorderFactory.createLineBorder(Color.RED));
// DragLayout by Rob Camick http://tips4java.wordpress.com/2011/10/23/drag-layout/
layer.getGlassPane().setLayout(new DragLayout());
layer.getGlassPane().add(toolTip);
layer.getGlassPane().setVisible(true);
}
}
// usage:
JTable table = new JTable(new AncientSwingTeam());
JLayer layer = new JLayer<JTable>(table, new ToolTipUI());
I have a JTable with a column containing a JComboBox.
I have an ItemListener attached to the JComboBox which acts upon any changes.
However, ItemListener does not have a method for obtaining the Row that the changed ComboBox is within.
I need to Row number in order to act upon another column in the same row when the ComboBox has a change.
Any help would be appreciated.
This is my Short and Concise code. What I am trying to accomplish, is obtaining the Table Row of the ComboBox when a the itemlistener picks up a change.
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class Example extends JFrame {
private static final long serialVersionUID = 1L;
public static int maxX, maxY;
public static final String[] columnHeads = {"Col 1", "Col 2", "Col 3"};
public static void main(String args[]) throws IOException {
Example example = new Example();
}
public Example() {
//Create Table Model
DefaultTableModel model = new DefaultTableModel();
for (int index = 0; index < columnHeads.length; index++) {
model.addColumn(columnHeads[index]);
}
//Create Table
JTable table = new JTable(model);
JScrollPane scrollPane = new JScrollPane(table);
//List for ComboBox
String[] list = {"Item1", "Item2", "Item3"};
//Create ComboBox
JComboBox itemTypes = attachComboBoxRenderer(table, 2, list);
//Attach Item Listener
itemTypes.addItemListener(new ComboBoxListener());
((DefaultTableModel) table.getModel()).insertRow(
table.getRowCount(), new Object[]{"C1", "C2", ""});
this.setTitle("Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container container = getContentPane();
//MAIN Panel
final JPanel main = new JPanel();
main.setLayout(new GridBagLayout());
main.add(scrollPane);
container.add(main);
this.pack();
this.setVisible(true);
}
public static JComboBox attachComboBoxRenderer(
JTable table, int column, Object[] values) {
JComboBox combo = new JComboBox(values);
TableColumn col = table.getColumnModel().getColumn(column);
col.setCellRenderer(new ComboBoxRenderer(values));
col.setCellEditor(new DefaultCellEditor(combo));
return combo;
}
}
class ComboBoxListener implements ItemListener {
private static final int SELECTED = 1;
#Override
public void itemStateChanged(ItemEvent e) {
// Get the affected item
Object item = e.getItem();
if (item.toString() != null
&& !"".equals(item.toString())
&& e.getStateChange() == SELECTED) {
System.out.println(item.toString() + " selected");
//How do I get Row in the Table of the ComboBox that was changed?
}
}
}
class ComboBoxRenderer extends JComboBox implements TableCellRenderer {
private static final long serialVersionUID = 1L;
public ComboBoxRenderer(Object[] items) {
super(items);
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
// Select the current value
setSelectedItem(value);
return this;
}
}
It sounds like you are Using a Combo Box as an Editor. If so, the TableCellEditor method, getTableCellEditorComponent(), includes the row as a parameter. There's a related example here.
Addendum: To change a value in the same row you've edited, just have the model return the correct value for the "other column" based on the related values in that row. Alternatively, update the related value in your model's setValueAt() method before firing the update, as shown in the example.
Addendum: Based on your example, the code below overrides the model's getValueAt() method to keep the dependent column synchronized with the item column.
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.IOException;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
/** #see http://stackoverflow.com/questions/7350445 */
public class DependentColumn extends JFrame {
private static final int DEPENDENT_COL = 1;
private static final int ITEM_COL = 2;
private static final String[] columnNames = {"Col 1", "Col 2", "Col 3"};
public static void main(String args[]) throws IOException {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
DependentColumn dc = new DependentColumn();
}
});
}
public DependentColumn() {
this.setTitle("Example");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create Model & Table
DefaultTableModel model = new DefaultTableModel(columnNames, 0) {
#Override
public Object getValueAt(int row, int col) {
if (col == DEPENDENT_COL) {
return "C2:" + this.getValueAt(row, ITEM_COL);
} else {
return super.getValueAt(row, col);
}
}
};
for (int i = 0; i < 16; i++) {
model.addRow(new Object[]{"C1", "C2", "Item1"});
}
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(new Dimension(320, 120));
//Create ComboBox
String[] items = {"Item1", "Item2", "Item3"};
JComboBox combo = new JComboBox(items);
TableColumn col = table.getColumnModel().getColumn(ITEM_COL);
col.setCellEditor(new DefaultCellEditor(combo));
combo.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
System.out.println(e.getItem() + " selected");
}
}
});
this.add(new JScrollPane(table));
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}