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.
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 already add a picture with JLabel in my JTable with TableCellRenderer. But how to add a border to the JLabel when mouse moves over the cell, on specific column and row?
This is the 1st renderer class:
public class RenderTabel implements TableCellRenderer{
#Override
public Component getTableCellRendererComponent(JTable table, Object
value,boolean isSelected, boolean hasFocus,int row, int column){
JLabel gambar=new JLabel();
String url="D:\\Kuliah Semester 4\\Pemrograman Berorientasi Objek\\DINUS BOOKSTORE\\image";
ImageIcon img=scalegmbr(url+"\\"+table.getModel().getValueAt(row, 0)+".png");
gambar.setIcon(img);
gambar.setText("");
gambar.setHorizontalAlignment(SwingConstants.CENTER);
table.setRowHeight(row, 50);
table.getColumnModel().getColumn(column).setPreferredWidth(80);
return gambar;
}
public ImageIcon scalegmbr(String file){
Image image=new ImageIcon(file).getImage();
return new ImageIcon(image.getScaledInstance(80,50,SCALE_SMOOTH));
}
}
This is 2nd renderer class:
public class RenderTabel1 implements TableCellRenderer{
#Override
public Component getTableCellRendererComponent(JTable table, Object
value,boolean isSelected, boolean hasFocus,int row, int column){
JLabel gambar=new JLabel();
String url="D:\\Kuliah Semester 4\\Pemrograman Berorientasi Objek\\DINUS BOOKSTORE\\image";
ImageIcon img=scalegmbr(url+"\\"+table.getModel().getValueAt(row, 0)+".png");
gambar.setIcon(img);
gambar.setText("");
gambar.setHorizontalAlignment(SwingConstants.CENTER);
gambar.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(200, 100, 52), 2));
table.setRowHeight(row, 50);
table.getColumnModel().getColumn(column).setPreferredWidth(80);
return gambar;
}
public ImageIcon scalegmbr(String file){
Image image=new ImageIcon(file).getImage();
return new ImageIcon(image.getScaledInstance(80,50,SCALE_SMOOTH));
}
}
and this is how I set the mouse enter and mouse clicked in my JTable:
private void tblbukuMouseEntered(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
tblbuku.getColumnModel().getColumn(6).setCellRenderer( new RenderTabel1());
}
private void tblbukuMouseExited(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
tblbuku.getColumnModel().getColumn(6).setCellRenderer( new RenderTabel());
}
But this adds a border to all cells in column 6 when mouse moves ofer a cell of that column. How to change it only into specific row and column when mouse entered that row and column?
So, for a particular column of your table, you want to paint a border on the cell that is hovered by the mouse (only the hovered cell, only in this column).
(edit: after clarification it appears that this question has been asked before -- I'm leaving my answer below as it might still help)
don't change the cell renderer dynamically, have only 1 renderer for that column, and handle that situation within the single renderer.
don't add listeners on the Component that is returned by the renderer: such listeners won't be triggered, as the component is only used for its paint()-ing logic.
instead, add a mouse motion listener on the table itself, and compute the coordinates of hovered cells with JTable's methods rowAtPoint and columnAtPoint, when mouse moves over table, or exits the area.
(irrelevant to problem at hand, but deserves a mention) Avoid creating a new JLabel for each call of your renderer, this is wasteful. Swing is single-thread, it's safe to reuse the same object (provided you don't forget to reset all its properties that might have changed between 2 calls)
Small demo that shows the effect:
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class SimpleTableDemo extends JPanel {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(SimpleTableDemo::createAndShowGUI);
}
private int
hoveredRow = -1,
hoveredColumn = -1;
SimpleTableDemo() {
super(new GridLayout(1,0));
String[] columnNames = {"First Name", "Last Name", "Sport", "# of Years", "Vegetarian"};
Object[][] data = {
{"Kathy", "Smith", "Snowboarding", 5, Boolean.FALSE},
{"John", "Doe", "Rowing", 3, Boolean.TRUE},
{"Sue", "Black", "Knitting", 2, Boolean.FALSE},
{"Jane", "White", "Speed reading", 20, Boolean.TRUE},
{"Joe", "Brown", "Pool", 10, Boolean.FALSE}
};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
table.getColumn("Sport").setCellRenderer(new MyCellRenderer());
table.addMouseMotionListener(new MouseAdapter() {
public void mouseMoved(MouseEvent e) {
Point p = e.getPoint();
hoveredRow = table.rowAtPoint(p);
hoveredColumn = table.columnAtPoint(p);
table.repaint();
}
public void mouseExited(MouseEvent e) {
hoveredRow = hoveredColumn = -1;
table.repaint();
}
});
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("SimpleTableDemo");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
SimpleTableDemo newContentPane = new SimpleTableDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
private class MyCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (hoveredColumn == column && hoveredRow == row) {
label.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
}
else {
label.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
return label;
}
}
}
Note 1: I'm using the default cell renderer, unlike you, but the same idea applies. The demo above is a generic example, that will be more useful as example to keep here than a specific solution for your case (for example, in my interpretation of the problem, I understand the details about icon are irrelevant).
Note 2: In the demo I repaint the whole visible area each time, but if you want to optimize it should be possible to repaint only 2 cells, that's an entire new question, see here for help about that.
I am sorry if this sounds likes a basic question but I am relatively new to java. I have a JComboBox which I populate from a database and then use it in a JTable. I do that using the following code:
itemEditortxt = new JComboBox(buildComboBoxmodel("SELECT item_name FROM items ORDER BY item_name"));
AutoCompleteDecorator.decorate(itemEditortxt);
TableColumn ledgerColumn = invoicePurchasedTable.getColumnModel().getColumn(0);
ledgerColumn.setCellEditor(new ComboBoxCellEditor(itemEditortxt));
I am trying to add key listeners to the JComboBox but for some reason they are not being called when I press any key when the focus is on the cell which uses the JComboBox. Following is how I add the litterers:
itemEditortxt.addKeyListener(new KeyListener() {
#Override
public void keyPressed(KeyEvent e) {System.out.print("line1");}
#Override
public void keyReleased(KeyEvent e) {System.out.print("line2");}
#Override
public void keyTyped(KeyEvent e) {System.out.print("line3");}
});
Can someone please tell me what Im doing wrong? Thanks.
Following is the SSCCE. There are two similar JComboBoxes, one is added normally and the other is used as a cell editor. In the first one the user can use the keyboard arrows and then press ENTER in order to make a selection. This is not the case for the one in the table. Thanks:
package sp2;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import javax.swing.table.*;
import org.jdesktop.swingx.autocomplete.*;
class InvoicePurchasedModel extends DefaultTableModel {
public InvoicePurchasedModel (Vector<Vector<Object>> data, Vector<String> columnNames) {
super(data, columnNames);
}
#Override
public Class getColumnClass(int col) {
if (col == 0)
return String.class;
else
return Double.class;
}
}
public class SP2 {
JFrame mainPage;
JTabbedPane jtp;
JPanel mainPanel;
JPanel purchasedInvoicesPanel;
RXTable invoicePurchasedTable;
DefaultTableModel invoicePurchasedtm;
JComboBox itemEditortxt;
JComboBox itemEditortxt2;
SP2() {
mainPage = new JFrame("System");
mainPage.getContentPane().setLayout(new GridLayout());
mainPage.setSize(1200, 1200);
mainPage.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
createTabs();
mainPage.setVisible(true);
}
void createTabs() {
jtp = new JTabbedPane();
mainPanel = new JPanel();
mainPanel.setLayout(new GridLayout());
mainPage.getContentPane().add(jtp);
purchasedInvoicesPanel = new JPanel();
jtp.addTab("Purchased", purchasedInvoicesPanel);
invoicePurchasedtm = buildInvoicePurchasedTableModel();
invoicePurchasedTable = new RXTable(invoicePurchasedtm) {
private final KeyStroke tabKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
public void changeSelection(int row, int column, boolean toggle, boolean extend)
{
super.changeSelection(row, column, toggle, extend);
if (editCellAt(row, column))
{
Component editor = getEditorComponent();
editor.requestFocusInWindow();
}
}
};
invoicePurchasedTable.setCellSelectionEnabled(true);
invoicePurchasedTable.setSelectAllForEdit(true);
purchasedInvoicesPanel.setLayout(new BoxLayout(purchasedInvoicesPanel, BoxLayout.PAGE_AXIS));
JPanel purchasedInvoicesPanel1 = new JPanel();
JPanel purchasedInvoicesPanel2 = new JPanel();
purchasedInvoicesPanel.add(purchasedInvoicesPanel1);
purchasedInvoicesPanel.add(purchasedInvoicesPanel2);
JScrollPane invoicePurchasedscrollPane = new JScrollPane(invoicePurchasedTable);
invoicePurchasedTable.setPreferredScrollableViewportSize(new Dimension(1000, 400));
String[] names = {"aa", "aa1", "aa2", "bb", "bb1", "bb2"};
itemEditortxt = new JComboBox(names);
itemEditortxt2 = new JComboBox(names);
AutoCompleteDecorator.decorate(itemEditortxt);
AutoCompleteDecorator.decorate(itemEditortxt2);
TableColumn ledgerColumn = invoicePurchasedTable.getColumnModel().getColumn(0);
ledgerColumn.setCellEditor(new ComboBoxCellEditor(itemEditortxt));
purchasedInvoicesPanel1.add(itemEditortxt2);
purchasedInvoicesPanel2.add(invoicePurchasedscrollPane);
}
public static DefaultTableModel buildInvoicePurchasedTableModel() {
Vector<String> columnNames = new Vector<String>();
columnNames.add("Item");
columnNames.add("Quantity");
columnNames.add("Unit Price");
columnNames.add("Amount");
Vector<Vector<Object>> data = new Vector<Vector<Object>>();
Vector<Object> vector = new Vector<Object>();
vector.add("");
vector.add(0.00);
vector.add(0.00);
vector.add(0.00);
data.add(vector);
return new InvoicePurchasedModel(data, columnNames);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new SP2();
}
});
}
}
"I am trying to add key listeners to the JComboBox but for some reason they are not being called when I press any key when the focus is on the cell which uses the JComboBox."
I think what you may be trying to do is add a listener to "text field" for the combobox. The first thing you need to do is actually get the editor component. then you can add a DocumentListener to the Document of the JTextComponent
JTextComponent editor = (JTextComponent) comboBox.getEditor().getEditorComponent();
editor.getDocument().addDocumentListener(new DocumentListener(){
...
});
Ok, so the problem was caused by the AutoCompleteDecorator. I deactivated it and used instead the AutoCompletion.enable(employeeDelete). Now the ENTER and TAB keys work as expected. I appreciate all the comments that helped me.
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.
I am working on a viewer, which uses a JList to show thumbnails of the pages of a document. The user can open a page by selecting it through in the JList, or throught other mechanisms, like entering the number in a text box.
When using the latter alternative, I want that the JList also selects the page. I do this using setSelectedIndex(), but this triggers an event, which causes the page to be loaded again, as if the user had clicked and selected the page in the JList, and this is causing me some problems.
How I see it, the index should be set some way (perhaps in the model) so that only the UI of the JList updates, without firing an event that the index has changed.
Is this possible? Or is there a better way to solve my issue?
You can remove all ListSelectionListener from the list, make a selection and then add them again.
You can create your own ListSelectionModel with a method that doesn't throw the event and set it as a selection model to your JList, and then use getSelectionModel().yourSelectIndexMethod(index).
You can also divert all your other methods of selection to the list, just find the corresponding entry if selecting the page by other means and select the item in the list. This way the item is selected and the page is loaded once.
Code for option 2:
public class ListTest extends JPanel{
private static final String[] items = new String[]{"1", "2", "3"};
private JList mylist;
private JComboBox myCombo;
private JTextArea myTA;
public ListTest() {
setLayout(new BorderLayout());
myCombo = new JComboBox(items);
myCombo.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
valueSelectedCombo(myCombo.getSelectedIndex());
}
});
JPanel pn = new JPanel();
pn.setLayout(new BoxLayout(pn, BoxLayout.X_AXIS));
pn.add(myCombo);
pn.add(Box.createHorizontalGlue());
pn.add(new JButton(new AbstractAction("Clear"){
#Override
public void actionPerformed(ActionEvent e){
myTA.setText("");
}
}));
add(pn, BorderLayout.NORTH);
add(new JScrollPane(getJList()), BorderLayout.WEST);
add(new JScrollPane(myTA = new JTextArea()), BorderLayout.CENTER);
}
private void valueSelectedList(int index){
myTA.setText(myTA.getText() + "\n" + items[index]);
}
private void valueSelectedCombo(int index){
myTA.setText(myTA.getText() + "\n" + items[index]);
((CustomSelectionModel)mylist.getSelectionModel()).setSelectionSilent(index);
}
private JList getJList(){
if (mylist == null){
mylist = new JList(items);
mylist.setSelectionModel(new CustomSelectionModel());
mylist.addListSelectionListener(new ListSelectionListener(){
#Override
public void valueChanged(ListSelectionEvent e){
if (!e.getValueIsAdjusting()){
valueSelectedList(mylist.getSelectedIndex());
}
}
});
mylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mylist.setPreferredSize(new Dimension(100, 106));
}
return mylist;
}
private static class CustomSelectionModel extends DefaultListSelectionModel{
private boolean isSilent = false;
public void setSelectionSilent(int firstIndex){
isSilent = true;
setSelectionInterval(firstIndex, firstIndex);
isSilent = false;
}
protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting){
if (isSilent){
return;
}
super.fireValueChanged(firstIndex, lastIndex, isAdjusting);
}
}
public static void main(String[] args){
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add content to the window.
frame.add(new ListTest());
// Display the window.
frame.pack();
frame.setSize(300, 200);
frame.setVisible(true);
}
}
It looks like setSelectedIndex() is just a convenient way to set the selection in the ListSelectionModel. Maybe your ListModel could flag or cache the result so it won't get loaded a second time.