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]);
}
}
}
Related
I will only post the part that matters.
I have created a JFrame with a JPanel in it, that contains some JTextFields, JTextAreas and a JList. I know want to add a JTable to show some results, but it will appear blank. I tried checking out some posts, but I wasn't able to fix it.
I entered 2 rows manually, but they wouldn't appear. Nor would the column names. Please help!
import javax.swing.*;
import javax.swing.table.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
public class GUI_Automata_Ex_1 extends JFrame {
public static int ScreenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
public static int ScreenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
public static int WindowWidth = ScreenWidth*3/4;
public static int WindowHeight = WindowWidth*9/16;
public static int unit = WindowWidth/160;
// tables used
static String[] columnNames = {"word", "length", "result"};
static Object[][] data = {{"abbaa", new Integer (5), "belongs"}, {"baabbb", new Integer (6), "does not belong"}};
public static JTable table_saved_words = new JTable(data, columnNames);
public static DefaultTableModel dtm_saved_words = new DefaultTableModel();
public static JScrollPane sp_saved_words;
public GUI_Automata_Ex_1 () {
// this will only run on ultrawide screens (e.g. 21:9 or 32:9) because the window is 16:9 optimized
if (ScreenWidth/2 > ScreenHeight) {
WindowHeight = ScreenHeight*3/4;
WindowWidth = WindowHeight*16/9;
unit = WindowWidth/160;
}
this.setTitle("Automata Theory 1st Excercise");
this.setBounds(ScreenWidth/2-WindowWidth/2,ScreenHeight/2-WindowHeight/2,WindowWidth,WindowHeight);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBackground(colorBG);
Board board = new Board();
this.setContentPane(board);
this.setLayout(null);
// TABLES
// settings for table_saved_words
table_saved_words.setBackground(Color.white);
table_saved_words.setFont(fontM);
table_saved_words.setModel(dtm_saved_words);
table_saved_words.setPreferredScrollableViewportSize(new Dimension(unit*86, unit*50));
table_saved_words.setFillsViewportHeight(true);
sp_saved_words = new JScrollPane(table_saved_words);
board.add(sp_saved_words);
sp_saved_words.setBounds(unit*68, unit*32, unit*86, unit*50);
sp_saved_words.setWheelScrollingEnabled(true);
sp_saved_words.setViewportView(table_saved_words);
sp_saved_words.setVisible(false);
dtm_saved_words.addRow(new Object[]{"aabbbbaa", 5, "belongs"});
}
public class Board extends JPanel {
public void paintComponent (Graphics g) {
}
}
}
}
Here is a screenshot:
https://i.stack.imgur.com/AHtLf.png
The JTable is on the bottom-right. I did not include the code part for the other J-components.
What I want to do after I get the lists to show is simply add some rows as the window runs (words that the user enters and either belong to a dictionary or not and their length), but the part I'm stuck on is getting the JTable to show the columns and data.
Your table model has no columns, so it never shows the data you add to it.
From the documentation for the zero-argument DefaultTableModel constructor
Constructs a default DefaultTableModel which is a table of zero columns and zero rows.
Initially, your table has a valid table model created automatically:
JTable table_saved_words = new JTable(data, columnNames);
But then you create a new model, with no columns:
DefaultTableModel dtm_saved_words = new DefaultTableModel();
I have a JTable and I need to verify the input of 2 columns. That is 2 specific columns need to contain proper values in every cell. If they do then I want to enable a button. I know how to enable a button but I don't know where/how to verify those 2 columns. What table model method would be the appropriate place to verify those cells and enable the button, if any? I'm thinking I need a listener for those cells and process them in loseFocus(). The only issue is if the user leaves the cursor in one of those cells so it never loses focus even though the entry is valid. This must be a relatively common practice so there must be a best programming practice to accomplish it. TIA.
A TableModelListener added to the table's model will notify you of any change in the model's data. Within the listener, you can iterate through the rows, extracting column data with getValueAt(...) method, and then enable/disable your JButton or its Action accordingly.
For example, in the following code there are two columns of integers. The ints in the B column must be greater than the A column for the data to be valid. If this is not the case (or if any value is null), the button is disabled:
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
public class TableFun extends JPanel {
private Integer[][] DATA = {{1, 2}, {3, 4}, {5, 6}};
private String[] COL_NAMES = {"A", "B"};
private DefaultTableModel model = new DefaultTableModel(DATA, COL_NAMES) {
public java.lang.Class<?> getColumnClass(int columnIndex) {
return Integer.class;
};
};
private JTable table = new JTable(model);
private JButton myButton = new JButton("My Button");
public TableFun() {
model.addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
boolean valid = true;
for (int row = 0; row < model.getRowCount(); row++) {
Integer valueA = (Integer) model.getValueAt(row, 0);
Integer valueB = (Integer) model.getValueAt(row, 1);
if (valueA == null || valueB == null || valueA.compareTo(valueB) > 0) {
valid = false;
}
}
myButton.setEnabled(valid);
}
});
JPanel panel = new JPanel();
panel.add(myButton);
setLayout(new BorderLayout());
add(new JScrollPane(table));
add(panel, BorderLayout.PAGE_END);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
TableFun mainPanel = new TableFun();
JFrame frame = new JFrame("TableFun");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
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'm attempting to create a JTable that looks like the mockup below:
The green corner is basically buffer-space for the red column and row headers. The cells don't need to be rendered in the colours pictured; however they need to be distinguishable from the rest of the 'white' cells in the table.
This table also is not editable or selectable; it's merely viewed by a user whilst it is updated.
I know this can be achieved using a DefaultTableModel with custom renders for rows 1,2 && cols 1,2 and adding +2 when setting and getting table values (accounting for the rows and columns that are being used as headers).
My questions are as follows:
Is there a cleaner way of doing this without polluting my table model with these static values used in headers?
I've read about extending table models but I'm not sure which class should I extend (DefaultTableModel, AbstractTableModel) and what methods I should override.
Input is limited to 20x20 so including the headers that's 22x22.
Also consider a JScrollPane containing a JPanel having GridLayout and containing 22x22 instances JLabel, or a suitable subclass. This scales easily to several thousand cells.
Addendum: If the need arises, CellRendererPane makes a good flyweight renderer, as suggested here.
If you go with JTable for rendering scalability,
This is no abuse; it is exactly how TableModel is intended to be used. TableModel models a rectangular matrix of whatever you decide. JTable is just an (efficiently rendered) view of that model.
I prefer AbstractTableModel, shown here, because Vector is rarely the desired data structure. Use whatever container makes your indexing most convenient. DefaultTableModel is handy and serves as a guide to extending AbstractTableModel. In particular, you'll need a setValueAt().
#Override
public void setValueAt(Object aValue, int row, int col) {
... // update your data structure
this.fireTableCellUpdated(row, col); // notify the view
}
longer comment, everything depends
1) if is possible for Columns
resize
reordering
2) if is possible for Columns
filtering
sorting
a. then you have look at two JTables, first JTable only with TableHeader, simple with removed rows and second full sized JTable with TableHeader and Columns and rows,
b. for interactions betweens two JTableHeader is there
TableColumnModelListener#columnMarginChanged(ChangeEvent e) and columnMoved(TableColumnModelEvent e)
c. everyting put to one JPanel inside JScrollPane
d. if you'll change numbers of rows or colums (or filtering / sorting) then you have to notified JPanel for rezize JTable#getPreferredScrollableViewportSize() + Dimension for ontop JTable only with TableHeader
very similair way as there (is everything that you needed)
(endless kudos for Rob)
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableFilterRow extends JFrame implements TableColumnModelListener {
private static final long serialVersionUID = 1L;
private JTable table;
private JPanel filterRow;
public TableFilterRow() {
table = new JTable(3, 5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
getContentPane().add(scrollPane);
table.getColumnModel().addColumnModelListener(this);
// Panel for text fields
filterRow = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
for (int i = 0; i < table.getColumnCount(); i++) {
filterRow.add(new JTextField(" Sum at - " + i));
}
columnMarginChanged(new ChangeEvent(table.getColumnModel()));
getContentPane().add(filterRow, BorderLayout.SOUTH);
}
// Implement TableColumnModelListener methods
// (Note: instead of implementing a listener you should be able to
// override the columnMarginChanged and columMoved methods of JTable)
#Override
public void columnMarginChanged(ChangeEvent e) {
TableColumnModel tcm = table.getColumnModel();
int columns = tcm.getColumnCount();
for (int i = 0; i < columns; i++) {
JTextField textField = (JTextField) filterRow.getComponent(i);
Dimension d = textField.getPreferredSize();
d.width = tcm.getColumn(i).getWidth();
textField.setPreferredSize(d);
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
filterRow.revalidate();
}
});
}
#Override
public void columnMoved(TableColumnModelEvent e) {
Component moved = filterRow.getComponent(e.getFromIndex());
filterRow.remove(e.getFromIndex());
filterRow.add(moved, e.getToIndex());
filterRow.validate();
}
#Override
public void columnAdded(TableColumnModelEvent e) {
}
#Override
public void columnRemoved(TableColumnModelEvent e) {
}
#Override
public void columnSelectionChanged(ListSelectionEvent e) {
}
public static void main(String[] args) {
JFrame frame = new TableFilterRow();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
3) otherwise look How to Use Raised Borders in the prepareRederer
4) this question has nothing to do with type of TableModel
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.