I have an object A that instance another object B.
I was wondering whether or not is possible to modify A with instructions in B.
In my circumstance, I have a Timetable (its code is under "Object A") that open (by InsertLesson.setVisible(true);) a Window to let the user compile its cells with lesson. At this time, the Window (InsertLesson, code under "object B") get the lesson selection by user but it is not able to write in the table that selection. How can I do?
Here the code
Object A:
public class TablePanel extends JPanel
{
private JTable table;
public Tabella()
{
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
table = new JTable(new MyTableModel());
table.setFillsViewportHeight(true);
table.setPreferredScrollableViewportSize(new Dimension(500, 100));
JScrollPane jps = new JScrollPane(table);
add(jps);
add(new JScrollPane(table));
table.setCellSelectionEnabled(true);
table.addMouseListener(
new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
int row = table.rowAtPoint(new Point(e.getX(), e.getY()));
int col = table.columnAtPoint(new Point(e.getX(), e.getY()));
if (col>0) {
if (e.getClickCount() > 1) {
if (row == 5 | row == 6)
{
JOptionPane.showMessageDialog(null, "Impossible to set lesson.");
return;
}
else {
table.getColumnName(col);
String day = table.getColumnName(col);
String hour = (String) table.getValueAt(row, 0);
InsertLesson cell = new InsertLesson(day, hour);
cel.setVisible(true);
}
}
}
}
}
);
}
private class MyTableModel extends AbstractTableModel {
private String[] columns = {"","Monday","Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
private String[][] data = {{"8:30 - 9:30","","","","","",""},
{"9:30 - 10:30","","","","","",""},
{"10:30 - 11:30","","","","","",""},
{"11:30 - 12:30","","","","","",""},
{"12:30 - 13:30","","","","","",""},
{"13:30 - 14:30","","","","","",""},
{"14:30 - 15:30","","","","","",""},
{"15:30 - 16:30","","","","","",""},
{"16:30 - 17:30","","","","","",""}};
public int getColumnCount() {
return columns.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columns[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
}
Object B (which has to modify A):
public InsertLesson (String day, String hour)
{
initialize(day, hour);
}
private void initialize(String day, String hour) {
this.setSize(600,200);
this.setTitle("Insert Lesson");
this.setLocationRelativeTo(null);
String[] lessons = {"math", "english", "art"};
String [] classrooms = {"class1", "class2"};
JPanel centralPnl = new JPanel();
this.getContentPane().add(centralPnl, BorderLayout.CENTER);
final JComboBox classBox = new JComboBox(classrooms );
centralPnl.add(classBox);
final JComboBox lessonsBox = new JComboBox(lessons);
centralPnl.add(lessonsBox);
JPanel southPnl = new JPanel();
this.getContentPane().add(southPnl, BorderLayout.SOUTH);
JButton insLessonBtn = new JButton("Insert Lesson");
southPnl.add(insLessonBtn);
lessonsBox.addItemListener(new ItemListener()
{
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
{
selectedLesson = lessonsBox.getSelectedItem().toString();
}
}
});
classBox.addItemListener(new ItemListener(){
#Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED)
{
selectedClass = classBox.getSelectedItem().toString();
}
}
});
class MouseSpy implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
JOptionPane.showMessageDialog(null,"Do something for modify table with\n"
+ "values of selectedLesson and selectedClass");
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
MouseListener listener = new MouseSpy();
insLessonBtn.addMouseListener(listener);
}
}
}
To update the table in A, B must invoke the method setValueAt() on the TableModel of A. Alternatively, add a method to your TableModel that does the update. A typical implementation of setValueAt() is seen here. If that doesn't help, please edit your question to include an sscce that exhibits the problem you encounter.
Addendum: I want to update the table … after the user press the … button.
As a concrete example using your TableModel, the Update button below updates the table's model with each press. Compare the implmentation of setValueAt () to the one cited above. The button's actionPerformed() method accesses a final reference to the TableModel in the enclosing scope, but you can pass a reference to your TableModel as a parameter to the constructor of InsertLesson.
Addendum: Would you write [it for] me?
No, but I will outline the approach, assuming a class InsertLesson,
TableModel model = new MyTableModel();
JTable table = new JTable(model);
InsertLesson cell = new InsertLesson(day, hour, model);
…
class InsertLesson {
TableModel model;
public InsertLesson(String day, String hour, TableModel model) {
this.model = model;
…
}
…
}
import java.awt.BorderLayout;
import java.awt.Dimension;
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.table.AbstractTableModel;
import javax.swing.table.TableModel;
/**
* #see http://stackoverflow.com/a/18764073/230513
*/
public class Test {
private static class MyTableModel extends AbstractTableModel {
private String[] columns = {
"Time", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
private String[][] data = {
{"8:30 - 9:30", "", "", "", "", ""},
{"9:30 - 10:30", "", "", "", "", ""},
{"10:30 - 11:30", "", "", "", "", ""},
{"11:30 - 12:30", "", "", "", "", ""},
{"12:30 - 13:30", "", "", "", "", ""},
{"13:30 - 14:30", "", "", "", "", ""},
{"14:30 - 15:30", "", "", "", "", ""},
{"15:30 - 16:30", "", "", "", "", ""},
{"16:30 - 17:30", "", "", "", "", ""}};
#Override
public int getColumnCount() {
return columns.length;
}
#Override
public int getRowCount() {
return data.length;
}
#Override
public String getColumnName(int col) {
return columns[col];
}
#Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
#Override
public void setValueAt(Object aValue, int row, int col) {
data[row][col] = (String) aValue;
fireTableCellUpdated(row, col);
}
}
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final TableModel model = new MyTableModel();
f.add(new JScrollPane(new JTable(model) {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(600, 128);
}
}));
f.add(new JButton(new AbstractAction("Update") {
#Override
public void actionPerformed(ActionEvent e) {
model.setValueAt(String.valueOf(e.getWhen() % 1000000), 1, 1);
}
}), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
Related
I am trying to disable selection of rows in all but 1 column in a JTable. (Layer Column in the example screenshot). In the other columns I have spinners and checkboxes that I want the user to be able to interact with, without effecting the selections in the Layer Column.
My initial attempt was to store up any selected rows as they occur, and then revert to that set of selected rows when a cell outside of column A is selected. It sort of works, but the problem is that it "flashes" when the other cell is selected, before it reverts it back. How can I prevent the "flash"?
Here is an example I set up to illustrate the problem:
public class TableTest {
static int[] selectedRows = new int[0];
final static String[] columns = new String[] { "Layer", "Enabled", "Read Only", "Storage" };
final static DefaultTableModel model = new DefaultTableModel(new Vector(), new Vector(Arrays.asList(columns))) {
#Override
public void setValueAt(Object obj, int row, int col) {
if (obj instanceof Boolean || obj instanceof Integer) {
Object localObject = super.getValueAt(row, col);
if (localObject instanceof Integer) {
Integer val = (Integer) localObject;
((SpinnerCell) obj).getSpinner().setValue(val);
} else if (localObject instanceof Boolean) {
Boolean val = (Boolean) localObject;
((CheckboxCell) obj).getCheckBox().setEnabled(val);
}
} else {
super.setValueAt(obj, row, col);
}
}
#Override
public boolean isCellEditable(int rowIndex, int colIndex) {
return colIndex != 0;
}
};
public static void main(String[] a) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTable table = new JTable(model) {
#Override
public TableCellRenderer getCellRenderer(final int rowIndex, int colIndex) {
int reaRowlIndex = convertRowIndexToModel(rowIndex);
int realColumnIndex = convertColumnIndexToModel(colIndex);
Object o = model.getValueAt(reaRowlIndex, realColumnIndex);
if (o instanceof TableCellRenderer) {
return (TableCellRenderer) o;
} else {
return super.getCellRenderer(reaRowlIndex, realColumnIndex);
}
}
//
#Override
public TableCellEditor getCellEditor(final int rowIndex, int colIndex) {
int reaRowlIndex = convertRowIndexToModel(rowIndex);
int realColumnIndex = convertColumnIndexToModel(colIndex);
Object o = model.getValueAt(reaRowlIndex, realColumnIndex);
if (o instanceof TableCellEditor) {
return (TableCellEditor) o;
} else {
return super.getCellEditor(reaRowlIndex, realColumnIndex);
}
}
};
table.getTableHeader().setReorderingAllowed(false);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent arg0) {
if (table.getSelectedColumn() == 0) {
selectedRows = table.getSelectedRows();
System.out.println("Selected Rows before " + Arrays.toString(selectedRows));
}
}
});
final ListSelectionModel columnListSelectionModel = table.getColumnModel().getSelectionModel();
columnListSelectionModel.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
if (table.getSelectedColumn() != 0) {
table.clearSelection();
System.out.println("Selected Rows during " + Arrays.toString(table.getSelectedRows()));
for (int i = 0; i < selectedRows.length; i++) {
table.getSelectionModel().addSelectionInterval(selectedRows[i], selectedRows[i]);
}
System.out.println("Selected Rows after " + Arrays.toString(table.getSelectedRows()));
}
}
});
model.addRow(new Object[] { "Bird", new CheckboxCell(new JCheckBox()),
new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Cat", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Dog", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Fish", new CheckboxCell(new JCheckBox()),
new CheckboxCell(new JCheckBox()), new SpinnerCell(new JSpinner()) });
model.addRow(new Object[] { "Pig", new CheckboxCell(new JCheckBox()), new CheckboxCell(new JCheckBox()),
new SpinnerCell(new JSpinner()) });
frame.add(new JScrollPane(table));
frame.setSize(300, 200);
frame.setVisible(true);
}
});
}
static class CheckboxCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
private static final long serialVersionUID = 1L;
private JCheckBox checkBox;
public CheckboxCell(JCheckBox inputCheckBox) {
checkBox = inputCheckBox;
}
#Override
public Object getCellEditorValue() {
return checkBox.isSelected();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
return checkBox;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
return checkBox;
}
public JCheckBox getCheckBox() {
return checkBox;
}
#Override
public boolean isCellEditable(EventObject evt) {
return true;
}
public String toString() {
return checkBox.isSelected() + "";
}
}
static class SpinnerCell extends AbstractCellEditor implements TableCellEditor, TableCellRenderer {
private static final long serialVersionUID = 1L;
private JSpinner editSpinner, renderSpinner;
public SpinnerCell() {
editSpinner = new JSpinner();
JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField();
tf.setForeground(Color.black);
renderSpinner = new JSpinner();
JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField();
tf2.setForeground(Color.black);
}
public SpinnerCell(JSpinner showSpinner) {
editSpinner = showSpinner;
JTextField tf = ((JSpinner.DefaultEditor) editSpinner.getEditor()).getTextField();
tf.setForeground(Color.black);
renderSpinner = showSpinner;
JTextField tf2 = ((JSpinner.DefaultEditor) renderSpinner.getEditor()).getTextField();
tf2.setForeground(Color.black);
}
#Override
public Object getCellEditorValue() {
return editSpinner.getValue();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row,
int column) {
return editSpinner;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
return renderSpinner;
}
public String toString() {
return editSpinner.getValue().toString();
}
public JSpinner getSpinner() {
return editSpinner;
}
#Override
public boolean isCellEditable(EventObject evt) {
return true;
}
}
}
Here's my short example:
Override JTable#changeSelection(...)
table.setCellSelectionEnabled(true);
Override ListSelectionModel#isSelectedIndex(...)
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableTest2 {
public JComponent makeUI() {
String[] columnNames = {"Layer", "Enabled", "Read Only"};
Object[][] data = {
{"Bird", true, false}, {"Cat", true, false},
{"Dog", true, false}, {"Fish", true, false}, {"Pig", true, false}
};
TableModel model = new DefaultTableModel(data, columnNames) {
#Override public Class<?> getColumnClass(int column) {
return getValueAt(0, column).getClass();
}
#Override public boolean isCellEditable(int row, int col) {
return col != 0;
}
};
JTable table = new JTable(model) {
#Override public void changeSelection(
int rowIndex, int columnIndex, boolean toggle, boolean extend) {
if (convertColumnIndexToModel(columnIndex) != 0) {
return;
}
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
};
table.setAutoCreateRowSorter(true);
table.setCellSelectionEnabled(true);
table.getColumnModel().setSelectionModel(new DefaultListSelectionModel() {
#Override public boolean isSelectedIndex(int index) {
return table.convertColumnIndexToModel(index) == 0;
}
});
return new JScrollPane(table);
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new TableTest2().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
If you want to prevent the flash you need to intercept an event before the selection is made because table.getSelectedColumn() only works after a selection has been made and you will always see that selection flash.
You could use a key listener to check for "Right Arrow", "Number Pad Right" and "End" key presses and a mouse clicked listener to check where the user is clicking and then change the event to instead select column A only.
For example see this answer showing how to check where a user clicks the mouse before a selection is made: https://stackoverflow.com/a/7351053/1270000
Then you just need to add some code to select the correct cell in column A instead, and don't forget to consume the event "e.consume()" to prevent the original event from completing with the wrong user selection.
Haven't tried it but you might be able to use a custom ListSelectionModel.
Here is an example that lets you toggle the selection of a row on/off:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ToggleListSelectionModel extends DefaultListSelectionModel
{
#Override
public void setSelectionInterval(int index0, int index1)
{
// Select multiple lines
if (index0 != index1)
{
super.addSelectionInterval(index0, index1);
return;
}
// Toggle selection of a single line
if (super.isSelectedIndex(index0))
{
super.removeSelectionInterval(index0, index0);
}
else
{
super.addSelectionInterval(index0, index0);
}
}
private static void createAndShowGUI()
{
String[] numbers = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
final JList<String> list = new JList<String>( numbers );
list.setVisibleRowCount( numbers.length );
list.setSelectionModel(new ToggleListSelectionModel());
// list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
JButton clear = new JButton("Clear");
clear.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
list.clearSelection();
}
});
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(list), BorderLayout.CENTER);
frame.add(clear, BorderLayout.PAGE_END);
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
In your case I guess you would need access to the table so you can use the getSelectedColumn() method.
You would remove the logic from the example above and replace it with something like:
if (table.getSelectedColumn() == 0)
super.setSelectionInterval(index0, index1);
I have a JTable with JCombobox editor for a certain column.
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class TablePanel extends JPanel {
public TablePanel() {
JTable table = new JTable(new MyTableModel());
setComboboxColumn(table.getColumnModel().getColumn(1));
add(new JScrollPane(table));
}
public void setComboboxColumn(TableColumn cbColumn) {
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItem("Item 1");
comboBox.addItem("Item 2");
comboBox.addItem("Item 3");
cbColumn.setCellEditor(new DefaultCellEditor(comboBox));
}
private static class MyTableModel extends AbstractTableModel {
private String[] columnNames = {"Normal cell", "Combobox cell"};
private Object[][] data = {
{"Cell 1", "Item 2"},
{"Cell 2", "Item 1"},
{"Cell 3", "Item 1"},
{"Cell 4", "Item 3"},
};
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
return data.length;
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
#Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
#Override
public boolean isCellEditable(int row, int col) {
return true;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
data[rowIndex][columnIndex] = aValue;
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("TablePanel");
frame.getContentPane().add(new TablePanel());
frame.pack();
frame.setVisible(true);
}
});
}
}
What happens now:
When I click for the first time on a cell of that column the combobox popup shows up immediately.
If I click other cells of the same column, the combobox shows up but the popup remains closed.
If I click on other cells and then back again on a cell of that column, the combobox popup shows up again immediately.
What I would like:
First click on the cell of that column: the combobox shows up, but the popup list remains closed.
Second click again on the same cell: the popup list shows up.
I know that I can use cellEditor.setClickCountToStart(2) but in this case the second click must be performed in a short time after the first one, and I would like to avoid this limit.
From the BasicComboPopup.Handler#mousePressed(...):
public void mousePressed(MouseEvent e) {
if (e.getSource() == list) {
return;
}
if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
return;
//...
togglePopup();
}
You might be able to use a AncestorListener:
When first click on the cell of that column, combobox.isEnabled()==false, so do not display popup, and later, AncestorListener#ancestorAdded() call combobox.setEnabled(true).
Second click again on the same cell: combobox.isEnabled()==true, the popup shows up.
If click on other cells: AncestorListener#ancestorRemoved() call combobox.setEnabled(false).
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
public class ComboBoxCellEditorTogglePopupTest {
private JComboBox<String> makeComboBox() {
JComboBox<String> combobox = new JComboBox<>();
combobox.addItem("Item 1");
combobox.addItem("Item 2");
combobox.addItem("Item 3");
return combobox;
}
public JComponent makeUI() {
String[] columnNames = {"Default", "setEnabled", "String"};
Object[][] data = {
{"Item 1", "Item 1", "aaa"}, {"Item 2", "Item 3", "bbb"}
};
JTable table = new JTable(new DefaultTableModel(data, columnNames));
table.setRowHeight(20);
table.getColumnModel().getColumn(0).setCellEditor(
new DefaultCellEditor(makeComboBox()));
final JComboBox<String> combobox = makeComboBox();
combobox.setEnabled(false);
combobox.addAncestorListener(new AncestorListener() {
#Override public void ancestorAdded(AncestorEvent e) {
System.out.println("ancestorAdded");
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
combobox.setEnabled(true);
}
});
}
#Override public void ancestorRemoved(AncestorEvent e) {
System.out.println("ancestorRemoved");
combobox.setEnabled(false);
}
#Override public void ancestorMoved(AncestorEvent e) {}
});
table.getColumnModel().getColumn(1).setCellEditor(
new DefaultCellEditor(combobox));
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ComboBoxCellEditorTogglePopupTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
I want to remove selected row of JTable by pressing only Delete button.
When I press Delete, selected cell becomes editable and my action(on jframe) does not receive KeyEvent.
Please, run this demo to see the effect:
public class TestTableKeyBinding extends JFrame {
// private static final int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
private JTable table;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestTableKeyBinding test = new TestTableKeyBinding();
test.setVisible(true);
}
});
}
TestTableKeyBinding() {
super();
initUI();
addKeyBindings();
}
private void initUI() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] headers = new String[] { "apples", "bananas" };
String[][] data = new String[][] { { "1", "2" }, { "4", "6" }, { "5", "7" }, { "1", "3" }, { "2", "11" } };
table = new JTable(data, headers);
table.setRowSelectionAllowed(true);
this.add(new JScrollPane(table));
this.pack();
this.setSize(new Dimension(300, 400));
}
private void addKeyBindings() {
// root maps
InputMap im = this.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = this.getRootPane().getActionMap();
// add custom action
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "save");
am.put("save", saveAction());
}
private AbstractAction saveAction() {
AbstractAction save = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(TestTableKeyBinding.this.table, "Action Triggered.");
table.editingCanceled(null);
table.editingStopped(null);
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
((DefaultTableModel) table.getModel()).removeRow(selectedRow);
}
}
};
return save;
}
}
Thanks for help!
I have tested your code and made some changes as mentioned below. Now it's working fine.
private void addKeyBindings() {
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
table.getActionMap().put("save", saveAction());
table.getInputMap(JComponent.WHEN_FOCUSED).put(keyStroke, "save");
}
Make one more change as mentioned below
table = new JTable(new DefaultTableModel(data,headers));
otherwise it will result in ClassCastException at below line
((DefaultTableModel) table.getModel()).removeRow(selectedRow);
Here is complete code:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.table.DefaultTableModel;
public class TestTableKeyBinding extends JFrame {
private JTable table;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestTableKeyBinding test = new TestTableKeyBinding();
test.setVisible(true);
}
});
}
TestTableKeyBinding() {
super();
initUI();
addKeyBindings();
}
private void initUI() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] headers = new String[] { "apples", "bananas" };
String[][] data = new String[][] { { "1", "2" }, { "4", "6" }, { "5", "7" }, { "1", "3" },
{ "2", "11" } };
table = new JTable(new DefaultTableModel(data,headers));
table.setRowSelectionAllowed(true);
this.add(new JScrollPane(table));
this.pack();
this.setSize(new Dimension(300, 400));
}
private void addKeyBindings() {
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
table.getActionMap().put("save", saveAction());
table.getInputMap(JComponent.WHEN_FOCUSED).put(keyStroke, "save");
}
private AbstractAction saveAction() {
AbstractAction save = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(TestTableKeyBinding.this.table, "Action Triggered.");
table.editingCanceled(null);
table.editingStopped(null);
int selectedRow = table.getSelectedRow();
if (selectedRow != -1) {
((DefaultTableModel) table.getModel()).removeRow(selectedRow);
}
}
};
return save;
}
}
I have implemented JTable frozen columns as below.When scrolling on a large dataset using vertical scrollbar, the columns in the frozen table are not aligned with the main table.Please help
JTable table,fixedTable
JScrollPane _scrl_table;
table = new JTable();
table.setName("MAIN_TABLE");
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setVisibleRowCount(8);
fixedTable = new JTable();
fixedTable.setName("FIXED_TABLE");
fixedTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
_scrl_table = new JScrollPane(table,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
fixedTable.setPreferredScrollableViewportSize(fixedTable.getPreferredSize());
_scrl_table.setRowHeaderView( fixedTable );
_scrl_table.setCorner(JScrollPane.UPPER_LEFT_CORNER, fixedTable.getTableHeader());
maybe there no reason to use two JTables
use proper RowNumberTable by camickr or RowHeader
you can to share AdjustmentListener, with or without BoundedRangeModel
example scrolling horizontal directions, have to change to the vertical
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class FixedRowExample extends JFrame {
private static final long serialVersionUID = 1L;
private Object[][] data;
private Object[] column;
private JTable fixedTable, table;
private int FIXED_NUM = 2;
public FixedRowExample() {
super("Fixed Row Example");
data = new Object[][]{
{"a", "", "", "", "", ""},
{"", "b", "", "", "", ""},
{"", "", "c", "", "", ""},
{"", "", "", "d", "", ""},
{"", "", "", "", "e", ""},
{"", "", "", "", "", "f"},
{"fixed1", "", "", "", "", "", "", ""},
{"fixed2", "", "", "", "", "", "", ""}};
column = new Object[]{"A", "B", "C", "D", "E", "F"};
AbstractTableModel model = new AbstractTableModel() {
private static final long serialVersionUID = 1L;
#Override
public int getColumnCount() {
return column.length;
}
#Override
public int getRowCount() {
return data.length - FIXED_NUM;
}
#Override
public String getColumnName(int col) {
return (String) column[col];
}
#Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
#Override
public void setValueAt(Object obj, int row, int col) {
data[row][col] = obj;
}
public boolean CellEditable(int row, int col) {
return true;
}
};
AbstractTableModel fixedModel = new AbstractTableModel() {
private static final long serialVersionUID = 1L;
#Override
public int getColumnCount() {
return column.length;
}
#Override
public int getRowCount() {
return FIXED_NUM;
}
#Override
public Object getValueAt(int row, int col) {
return data[row + (data.length - FIXED_NUM)][col];
}
};
table = new JTable(model);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
fixedTable = new JTable(fixedModel);
fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
fixedTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scroll = new JScrollPane(table);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JScrollPane fixedScroll = new JScrollPane(fixedTable) {
private static final long serialVersionUID = 1L;
#Override
public void setColumnHeaderView(Component view) {
} // work around
}; // fixedScroll.setColumnHeader(null);
fixedScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JScrollBar bar = fixedScroll.getVerticalScrollBar();
JScrollBar dummyBar = new JScrollBar() {
private static final long serialVersionUID = 1L;
#Override
public void paint(Graphics g) {
}
};
dummyBar.setPreferredSize(bar.getPreferredSize());
fixedScroll.setVerticalScrollBar(dummyBar);
final JScrollBar bar1 = scroll.getHorizontalScrollBar();
JScrollBar bar2 = fixedScroll.getHorizontalScrollBar();
bar2.addAdjustmentListener(new AdjustmentListener() {
#Override
public void adjustmentValueChanged(AdjustmentEvent e) {
bar1.setValue(e.getValue());
}
});
scroll.setPreferredSize(new Dimension(400, 100));
fixedScroll.setPreferredSize(new Dimension(400, 52));
getContentPane().add(scroll, BorderLayout.CENTER);
getContentPane().add(fixedScroll, BorderLayout.SOUTH);
}
public static void main(String[] args) {
FixedRowExample frame = new FixedRowExample();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
EDIT
I've tried changing the table model back to DefaultTableModel but I am getting an Exception when compiling my code and I can't figure out why!
Here's my table init:
jTable1.setModel(new Table1Model());
jTable1.setDefaultRenderer(Color.class,new ColorRenderer(true));
jTable1.getColumnModel().getColumn(5).setCellEditor(new ColorEditor());
My class extending the model:
class Table1Model extends DefaultTableModel {
//private String[] columnNames = {"Station #",
private Object[] columnNames = {"Station #",
"Name",
"avg Time",
"Buffer",
"Buffer Parts",
"Color"};
private Object[][] data = {
{"1", "Station 1",
new Integer(10), false, new Integer(0), Color.red},
{"2", "Station 2",
new Integer(10), false, new Integer(0), Color.blue},
{"3", "Station 3",
new Integer(10), false, new Integer(0), Color.green},
{"4", "Station 4",
new Integer(10), false, new Integer(0), Color.orange},
{"5", "Station 5",
new Integer(10), false, new Integer(0), Color.black}
};
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
//int length = data.length;
//int datalength = Integer.parseInt(length);
return data.length;
}
#Override
public String getColumnName(int col) {
return columnNames[col].toString();
}
#Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn't implement this method,
* then the last column would contain text ("true"/"false"),
* rather than a check box.
*/
#Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's
* editable.
*/
#Override
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
if (col == 0) { return false; }
else if (col == 4) {
boolean di = (Boolean) getValueAt(row,(col-1));
if (!di) { return false; }
else { return true; }
}
else { return true; }
}
/*
* Don't need to implement this method unless your table's
* data can change.
*/
#Override
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
/*public void removeRow(int row) {
data.removeRow(row);
}*/
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
This generates the following error:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at SimGui$Table1Model.getRowCount(SimGui.java:863)
at javax.swing.table.DefaultTableModel.setDataVector(DefaultTableModel.java:224)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:124)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:106)
at javax.swing.table.DefaultTableModel.<init>(DefaultTableModel.java:86)
at SimGui$Table1Model.<init>(SimGui.java:832)
at SimGui.initComponents(SimGui.java:265)
at SimGui.<init>(SimGui.java:34)
at SimGui$16.run(SimGui.java:789)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:701)
at java.awt.EventQueue.access$000(EventQueue.java:102)
at java.awt.EventQueue$3.run(EventQueue.java:662)
at java.awt.EventQueue$3.run(EventQueue.java:660)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:671)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:97)
Can you help me figure out what's wrong?
Also, will I be able to use the ColorEditor with the DefaultTableModel?
You have two obvious choices here: either give your class a getVectorData() method or else give it another similarly useful method that allows you to extract the nucleus of your data from your model. You probably shouldn't use an AbstractTableModel variable though but rather use a variable of your own custom type that extends AbstractTableModel to allow you to be able to call custom methods of your model.
i.e.,
MyTableModel model = (MyTableModel)jTable1.getModel();
SomeCollection myKeyData = model.getMyKeyData();
Also, this statement:
I have recently created my own class to extend AbstractTableModel to be able to insert some logic on isCellEditable and setValueAt.
Doesn't make sense to me since you could always use a DefaultTableModel and simply override those two methods. But if you use a DefaultTableModel, don't also have it hold the 2D array of object as you're trying to do. Rather feed the data into its internal data via the appropriate constructor or via its addRow(...) method. Else you lose all the power that DefaultTableModel has to offer.
Edit
If you want to use a DefaultTableModel to leverage its methods, then you cannot use a separate data "nucleus" for your model (here your Object[][]), but instead must load your data into the model that is held inside of the DefaultTableModel super class. This can be done either via the correct super constructor or by adding rows of data using its addRow(...) method.
For example, here I load your data into a DefaultTableModel override:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.table.*;
public class TableModelTest extends JPanel {
private static final Object[][] DATA = {
{ "1", "Station 1", new Integer(10), false, new Integer(0), Color.red },
{ "2", "Station 2", new Integer(10), false, new Integer(0), Color.blue },
{ "3", "Station 3", new Integer(10), false, new Integer(0),
Color.green },
{ "4", "Station 4", new Integer(10), false, new Integer(0),
Color.orange },
{ "5", "Station 5", new Integer(10), false, new Integer(0),
Color.black } };
private MyTableModel myTableModel = new MyTableModel(DATA);
private JTable table = new JTable(myTableModel);
public TableModelTest() {
setLayout(new BorderLayout());
add(new JScrollPane(table), BorderLayout.CENTER);
table.getColumnModel().getColumn(5)
.setCellRenderer(new ColorCellRenderer());
table.getColumnModel().getColumn(5).setCellEditor(new ColorCellEditor());
}
private static void createAndShowGui() {
TableModelTest mainPanel = new TableModelTest();
JFrame frame = new JFrame("TableModelTest");
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();
}
});
}
}
class ColorCellEditor extends AbstractCellEditor implements TableCellEditor {
Color currentColor;
JButton button;
JColorChooser colorChooser;
JDialog dialog;
protected static final String EDIT = "edit";
public ColorCellEditor() {
ActionListener actionListener = new MyActionListener();
button = new JButton();
button.setActionCommand(EDIT);
button.addActionListener(actionListener);
button.setBorderPainted(false);
colorChooser = new JColorChooser();
dialog = JColorChooser.createDialog(button, "Pick a Color", true,
colorChooser, actionListener, null);
}
private class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
if (EDIT.equals(e.getActionCommand())) {
button.setBackground(currentColor);
colorChooser.setColor(currentColor);
dialog.setVisible(true);
fireEditingStopped();
} else {
currentColor = colorChooser.getColor();
}
}
}
public Object getCellEditorValue() {
return currentColor;
}
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
currentColor = (Color) value;
return button;
}
}
class ColorCellRenderer implements TableCellRenderer {
private static final int IMG_WIDTH = 70;
private static final int IMG_HEIGHT = 20;
private JLabel label = new JLabel();
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean arg2, boolean arg3, int arg4, int arg5) {
Color color = (Color) value;
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = img.getGraphics();
g.setColor(color);
g.fillRect(0, 0, IMG_WIDTH, IMG_HEIGHT);
g.dispose();
ImageIcon icon = new ImageIcon(img);
label.setIcon(icon);
return label;
}
}
class MyTableModel extends DefaultTableModel {
private static final String[] COLUMN_NAMES = { "Station #", "Name",
"avg Time", "Buffer", "Buffer Parts", "Color" };
public MyTableModel(Object[][] data) {
super(data, COLUMN_NAMES);
}
#Override
public boolean isCellEditable(int row, int col) {
if (col == 0) {
return false;
} else if (col == 4) {
boolean di = (Boolean) getValueAt(row, (col - 1));
if (!di) {
return false;
} else {
return true;
}
} else {
return true;
}
}
public void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i = 0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j = 0; j < numCols; j++) {
Object datum = getValueAt(i, j);
// System.out.print(" " + data[i][j]);
System.out.print(" " + datum);
}
System.out.println();
}
System.out.println("--------------------------");
}
}