I have a keylistener on jtable so that when someone presses enter some calculations happen. However, this only happens if the person is not editing. I would like to apply this action when a person finishes editing a cell and is pressing enter to finish and close the editing.
I cannot figure this out, anyone ever did this or know how to?
Basically, now for the action to be done, people must press enter twice, one to end the editing and another for the action that I want to happen, I would like to make it needed only once, while editing.
Thank you
You could customize your own editor. Using DefaultCellEditor Instead using KeyListener you should use KeyBindings.
See this example.
JTable table = new JTable(myModel);
JTextField cell = new JTextField();
final TableCellEditor cellEditor = new DefaultCellEditor(cell);
table.getColumnModel().getColumn(column).setCellEditor(cellEditor);
InputMap iMap = cell.getInputMap(JComponent.WHEN_FOCUSED);
iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), KeyEvent.getKeyText(KeyEvent.VK_ENTER));
ActionMap aMap = cell.getActionMap();
aMap.put(KeyEvent.getKeyText(KeyEvent.VK_ENTER), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
if(callSomeOperationsIsOk()){
cellEditor.stopCellEditing();
}else{
cellEditor.cancelCellEditing();
}
}
});
}
Read more in tutorials How to use Tables, and perhaps you have the same trouble that i have see my previous question
I have a keylistener on jtable so that when someone presses enter some
calculations happen. However, this only happens if the person is not
editing. I would like to apply this action when a person finishes
editing a cell and is pressing enter to finish and close the editing.
TableCellEditor hasn't something with KeyListener added to JTable
Basically, now for the action to be done, people must press enter
twice, one to end the editing and another for the action that I want
to happen, I would like to make it needed only once, while editing.
JComponents (used as TableCellEditor) by default to react to ENTER key pressed
don't to put JComponent to the TableModel, there should be stored only value painted by TableCellRenderer and initial value for TableCellEditor
TableCellEditor is temporarily JComponent, you have to add KeyBindings to invoke stopCellEditing in the case that JComponents used as TableCellEditor doesn't to react to ENTER key pressed
this issue isn't about standards, for better help sooner post an SSCCE, short, runnable, compilable, with hardcoded valur for JTable/XxxTableModel
before anything to read Oracle tutorial How to use Tables, especially parts
Creating a Table Model
Concepts: Editors and Renderers
Using Custom Renderers
You can override JTable.editingStopped, which is invoked when editing is finished and apply your actions in that method.
EDIT:
JTable.editingStopped was not designed for application extension. To avoid complications, in particular platform dependent ones, a better approach is to override model's setValueAt or register a TableModelListener. Here is an example:
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class DemoTable3 {
private static void createAndShowUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object[][] rows = { { "Column 1", "Column 2" },
{ "Column 1", "Column 2" } };
Object[] columns = { "Column 1", "Column 2" };
DefaultTableModel model = new DefaultTableModel(rows, columns);
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
System.out.println("apply additional action");
}
});
JTable table = new JTable(model);
frame.add(new JScrollPane(table));
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Another alternative is to add CellEditorListener to catch editingStopped events. For example:
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.table.DefaultTableModel;
public class DemoTable2 {
private static void createAndShowUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object[][] rows = { { "Column 1", "Column 2" },
{ "Column 1", "Column 2" } };
Object[] columns = { "Column 1", "Column 2" };
final JTable table = new JTable(new DefaultTableModel(rows, columns));
table.getDefaultEditor(String.class).addCellEditorListener(
new CellEditorListener() {
public void editingCanceled(ChangeEvent e) {
System.out.println("editingCanceled");
}
public void editingStopped(ChangeEvent e) {
System.out.println("editingStopped: apply additional action");
}
});
frame.add(new JScrollPane(table));
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
Also look at a Table Cell Listener by #camickr which offers custom processing of the edits.
Related
First question here, hopefully I do this correctly.
Below is a minimal example of my problem that I quickly whipped up representative of my project. I have created a custom renderer for a JList containing some objects (in the example I've used Strings for illustrative purposes). My issue is that, as far as I can tell, if I add a ListSelectionListener to the list, the family of setSelected...() methods do not fire events that trigger the conditional if(e.getValueIsAdjusting()).
In the example below, the problem is immediately obvious upon starting the program: even though list.setSelectedIndex(2); is called after assigning the JLabel's selected text as "none", it does not change until you, the user, click the list items. In fact, it must be a different item to the one currently selected.
I would like this functionality so that the flow of my program will be that, after a user adds/removes items from the list, the "view" is immediately updated once the selected list item is changed.
Have I done something wrong, or am I approaching this incorrectly?
Thank you. :)
import java.awt.BorderLayout;
import java.beans.*;
import javax.swing.*;
public class Example implements PropertyChangeListener {
String ITEM_SELECTED = "Item Selected";
String DELETE_SELECTED = "Delete Selected";
PropertyChangeSupport pcs = new PropertyChangeSupport(this);
JLabel selected;
JList<String> list;
DefaultListModel<String> model;
public static void main(String[] args) {
new Example();
}
public Example() {
JFrame frame = new JFrame();
JPanel mainPanel = new JPanel(new BorderLayout());
model = new DefaultListModel<>();
model.addElement("Entry 1");
model.addElement("Entry 2");
model.addElement("Entry 3");
model.addElement("Entry 4");
model.addElement("Entry 5");
list = new JList<>(model);
selected = new JLabel("Currently selected: none");
list.setSelectedIndex(2);
list.addListSelectionListener(e -> {
if(e.getValueIsAdjusting()) {
pcs.firePropertyChange(ITEM_SELECTED, null, null);
}
}
);
JButton removeItem = new JButton("Remove item");
removeItem.addActionListener(e -> {
pcs.firePropertyChange(DELETE_SELECTED, null, null);
}
);
mainPanel.add(new JScrollPane(list), BorderLayout.WEST);
mainPanel.add(removeItem, BorderLayout.NORTH);
mainPanel.add(selected, BorderLayout.CENTER);
pcs.addPropertyChangeListener(this);
frame.add(mainPanel);
frame.setSize(300, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
String event = (String) evt.getPropertyName();
if (event.equals(ITEM_SELECTED)) {
selected.setText("Currently selected: " + list.getSelectedValue());
}
if (event.equals(DELETE_SELECTED)) {
int selectedIndex = list.getSelectedIndex();
model.removeElement(list.getSelectedValue());
if (selectedIndex > 0)
list.setSelectedIndex(selectedIndex-1);
else if (selectedIndex == 0)
list.setSelectedIndex(selectedIndex);
}
}
}
The e.getValueIsAdjusting() method checks if the value is still being adjusted. In your case it looks like the event fired by list.setSelectedIndex(2) would have e.getValueIsAdjusting() return false.
You can probably fix this by doing the opposite check : !e.getValueIsAdjusting().
This will then check that any change responded to is the final one for that chain of events.
I have a JPanel holding a JButton and JScrollPane (in turn holding a JTable) and am currently running into two issues which I believe are related:
The JButton listener's actionPerformed() method is not invoked upon click. The only way in which I can get it to be invoked is by calling doClick() on the JButton. The JButton color changes upon hover but no click animation is shown when the mouse is pressed.
Secondly, if a cell is clicked within the JTable, the cell located 2 rows down in the same column registers the click instead. This offset does not occur when clicking in the column headers (i.e. to adjust cell widths), only when within the cell area.
Left-hand panel. Click position circled
public class InventoryPanel extends JPanel {
// Parent Business object reference for communication and JFrame
private Business parent;
private AddItemPanel addItemPanel;
// Inventory table items
private DefaultTableModel inventoryModel;
private JTable inventoryTable;
private JScrollPane inventoryScrollPane;
private JLabel updateLbl;
private JButton addItemBtn;
// Columns for inventory table
private static final String[] INVENTORY_COLUMNS = {"Item","Stock","Restocking Level","Edit"};
public InventoryPanel(Business parent) {
this.parent = parent;
initGUI();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
//doStuff
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace(new PrintStream(System.out));
}
}
}
}).start();
}
// INITIALISES GUI
public void initGUI() {
this.setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
this.setBorder(BorderFactory.createLineBorder(Color.BLACK));
JLabel titleLabel = new JLabel("<html><B>Inventory</B></html>");
this.add(titleLabel);
// Create empty inventory table
inventoryModel = new DefaultTableModel(new Object[3][4],INVENTORY_COLUMNS);
inventoryTable = new JTable(inventoryModel);
inventoryScrollPane = new JScrollPane(inventoryTable);
// Create button to allow items to be added
addItemBtn = new JButton("Add item");
addItemBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("ADD ITEM PRESSED");
}
});
updateLbl = new JLabel("Loading inventory...");
this.add(addItemBtn);
this.add(inventoryScrollPane);
this.add(updateLbl);
}
I've tried removing the table from the panel to see if that solves the JButton issue and visa-versa, but no luck. I've also tried changing the project JDK but no luck there either.
There are other JPanels adjacent to the troublesome one in a JFrame which work perfectly fine. Any ideas?
Edit: I can create a working instance of the InventoryPanel alone in a frame in another project, as mentioned in the comments. However the exact same code (no calls being made to other objects/methods) in the current project now produces ClassCastExceptions. After some googling this seems to be due to non-EDT threads updating the GUI.
However there is no use of the Business class, and all GUI operations are performed using the SwingUtilities.invokeLater() method like so:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("test");
frame.add(new InventoryPanel());
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
Note: the no-argument constructor InventoryPanel() just calls initGUI().
Thanks for the help so far...still very confused by this.
I have a JTable with three columns. For each rows I have a word, its type and the number of occurrences; for example in the next picture, the String "Rosing Prize" is present two times.
Starting from this JTable I want to build an histogram that takes as input the first and the last column. The first column is the name of bars and the last is its height; when the user selects some rows, they are represent in the histogram.
For example in this situation I have 4 rows selected:
The output are four J-Frames: the first with just one bar (that represents the first row); in the second J-Frame I have two bars (first and second row); in the third JFrame there are 3 bars for first, second and third row and, finally in the forth and last JFrame I have the correct output:
I thought about two possibilities to fix this problem:
to add a Jbutton and after one presses it the selected rows are drawn in the histogram
to add all JFrame to an ArrayList and to print only the last.
Are there better solutions?
I added a ListSelectionListener listener to my table model.
In your ListSelectionListener, update the chart's dataset only when getValueIsAdjusting() is false. This will defer updates until the selection is stable.
If I understand your question right, ListSelectionListener will solve your problem.
Define a selection listener first:
class MySelectionListener implements ListSelectionListener {
Then add it to your table's selection model:
MySelectionListener selectionListener = new MySelectionListener();
table.getSelectionModel().addListSelectionListener(selectionListener);
Edit:
Create a MouseListener. Then add it to your table. Here is a working sample code:
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JTable;
public class TableTest {
JFrame window = new JFrame();
private TableTest() {
createWindow();
}
public void createWindow() {
Object rowData[][] = { { "Row1-Column1", "Row1-Column2", "Row1-Column3" },
{ "Row2-Column1", "Row2-Column2", "Row2-Column3" },
{ "Row3-Column1", "Row3-Column2", "Row3-Column3" } };
Object columnNames[] = { "Column One", "Column Two", "Column Three" };
JTable table = new JTable(rowData, columnNames);
table.addMouseListener(new SelectionListener(table));
window.add(table);
window.pack();
window.setVisible(true);
}
public static void main(String[] args) {
new TableTest().createWindow();
}
}
class SelectionListener extends MouseAdapter {
JTable table;
public SelectionListener(JTable table) {
this.table = table;
}
#Override
public void mouseReleased(MouseEvent e) {
int[] rows = table.getSelectedRows();
for (int i = 0; i < rows.length; i++) {
System.out.println(rows[i]);
}
}
}
I have a Jtable allow editing and inserting.
When editing a column, lets say "ID", I want to have a check, if the inserted record or the record after editing have duplicat "ID" column value with the other records. it is not allowed.
actually, to be specific, if someone is editing the ID column, when he hit the "Enter" or move the focus to another cell, perform a check, if the ID is duplicate, then disallow the edition.
how can I do this?
if someone is editing the ID column, when he hit the "Enter" or move the focus to another cell, perform a check, if the ID is duplicate, then disallow the edition.
Create a custom editor to validate the id before it is saved to the model. Here is a simple example to get your started:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
try
{
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
Extend DefaultTableModel and override the add and update methods checking for duplication and use this class for your JTable.
Extend AbstractTableModel and arrange for it to contain a Set, which precludes duplicate elements. As suggested by #camickr, you'll want a custom cell editor to communicate the results failed additions.
Is there an easy way to manipulate the controls on a JTable to give different functionality when there is a keyboard button pressed (ie. CTRL button) and a row is selected? I've been asked to create a table where the CTRL + Click (mouse click) on a row will only deselect a selected row, never select a row. If the user CTRL + Clicks an unselected row, nothing will happen.
I've been able to create a table, and disable functions like CTRL + A (select all), and i've been able to check if the control button is pressed when a MouseEvent is generated, but I can't seem to figure out how the CTRL + Click can be adjusted. Here's some code:
package nicky;
import javax.swing.*;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
public class TableTester extends JPanel {
public TableTester() {
super(new GridLayout(1,0));
final String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
final Object[][] data = {
{"Tom", "Roberts","Athletic", new Integer(5), new Boolean(false)},
{"Sarah", "Watt", "Football", new Integer(3), new Boolean(true)},
{"Laura", "Brown", "Swimming", new Integer(2), new Boolean(false)},
{"Simon", "Smith", "Tennis", new Integer(20), new Boolean(true)},
{"Paul", "Jones", "Rugby", new Integer(10), new Boolean(false)}
};
JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 100));
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.addMouseListener(new MouseListener(){
public void mouseEntered(MouseEvent me){}
public void mouseExited(MouseEvent me){}
public void mouseReleased(MouseEvent me){}
public void mouseClicked(MouseEvent me){}
public void mousePressed(MouseEvent me){
if (me.isControlDown()){
System.out.println("This is working ");
}
}
});
InputMap inputMap = table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK);
inputMap.put(keyStroke, "none");
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
private static void createAndShowGUI() {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("TableTester");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TableTester newContentPane = new TableTester();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
In the mousePressed method I've played around with getting all the selected rows from the table, and was then going to check if the newly clicked row was in the selectedRows... However, I'm not sure if there is a way to see which row is associated with the MouseEvent.
(Also, I know expected behaviour such as this shouldn't be played around with too much, but it's to replicate a legacy system in the company)
Any ideas/suggestions would be appreciated!
OK, second take (I left the first one as it might interest somebody for some other usage, who know? Say it is there for educational purpose... :-)).
I had a look at the source code of JTable and found out that mouse events are handled by the look and feel. Knowing how it handles the control key, I could safely override the changeSelection method to do what you need.
I find requirements a bit strange (you can still use Shift+click, no?) but I don't know context.
class SpecialTable extends JTable
{
SpecialTable(Object[][] data, String[] columnNames)
{
super(data, columnNames);
// That's already the default
// setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
}
/**
* Called by javax.swing.plaf.basic.BasicTableUI.Handler.adjustSelection(MouseEvent)
* like: table.changeSelection(pressedRow, pressedCol, e.isControlDown(), e.isShiftDown());
*/
#Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
if (toggle && !isRowSelected(rowIndex))
return; // Don't do the selection
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
}
Much simpler and exactly what you need!
BTW, thanks for providing such simple good test case, I might have not tried if I had to write it myself... :-D It was an interesting and learning challenge.
I had success with the following, although I am not sure that's the best method...
class SpecialTable extends JTable
{
boolean bIsControlDown;
int clickedRow;
SpecialTable(Object[][] data, String[] columnNames)
{
super(data, columnNames);
// setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
getSelectionModel().addListSelectionListener(this);
addMouseListener(new MouseInputAdapter()
{
public void mousePressed(MouseEvent me)
{
bIsControlDown = me.isControlDown();
clickedRow = rowAtPoint(me.getPoint());
}
});
}
public void valueChanged(ListSelectionEvent evt)
{
super.valueChanged(evt);
if (bIsControlDown)
{
if (!evt.getValueIsAdjusting())
{
// System.out.println(evt);
// System.out.println("=> " + clickedRow);
getSelectionModel().removeSelectionInterval(clickedRow, clickedRow);
}
}
}
}
Replace the lines defining table in your code with only:
JTable table = new SpecialTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 100));
When you control-click an unselected row, it is briefly selected, then unselected.