I have a problem to update JXTable's rows height. I have tested an example from this post (Setting the height of a row in a JTable in java) :
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class DemoTable {
private static void createAndShowGUI() {
JFrame frame = new JFrame("DemoTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel();
model.setColumnIdentifiers(new Object[] {
"Column 1", "Column 2", "Column 3" });
JTable table = new JTable(model);
for (int count = 0; count < 3; count++){
model.insertRow(count, new Object[] { count, "name", "age"});
}
table.setRowHeight(1, 30);
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() {
createAndShowGUI();
}
});
}
}
This demo works perfectly, but if I change JTable to JXTable (and I need it for my project), the second row is not updated, the size is 15 for all. If I use setRowHeight(30) instead of setRowHeight(1, 30) with JXTable, it works but all the rows are updated.
Is this a bug ? How can I solve this problem ?
I'm using the library swingx-0.9.2.jar
Thanks
Related
I'm using sortable columns for my JTable:
table.setAutoCreateRowSorter(true);
The issue is that after the user click on a column header there's no way to remove the arrow. Even If I delete all the rows in the table.
I tried to do the opposite but it didn't work:
table.setAutoCreateRowSorter(false);
The fact the arrow is not removed seem to be a painting issue. Calling table.getTableHeader().repaint() seems to make the arrow go away.
Full example:
public class JTableSortRestore {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
runGui();
});
}
private static void runGui() {
JFrame frame = new JFrame("");
frame.setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Col");
model.addRow(new String[] { "BBB" });
model.addRow(new String[] { "AAA" });
model.addRow(new String[] { "CCC" });
JTable table = new JTable(model);
table.setAutoCreateRowSorter(true);
frame.add(new JScrollPane(table));
JButton restoreButton = new JButton("Restore sorting");
restoreButton.addActionListener(e -> {
table.setAutoCreateRowSorter(false);
table.setAutoCreateRowSorter(true);
table.getTableHeader().repaint();
});
frame.add(restoreButton, BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
I've looked everywhere but can't seem to figure this out. I just want to pull out a cell's value from my JTable when a user clicks on it.
However at the moment I am getting -1 so I suppose double clicking results in no row being detected. Here is the code:
import java.awt.*;
import java.awt.event.*;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.table.TableColumn;
public class CardLayoutExample {
private static JScrollPane scrollPane;
public static void main(String[] arguments) throws SQLException {
// main window
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame window = new JFrame("CardLayout Example");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(1500,800);
window.getContentPane().setLayout(new BorderLayout());
final CardLayout cardLayout = new CardLayout();
final JPanel cardPanel = new JPanel(cardLayout);
JPanel card3 = new JPanel();
cardPanel.add(card3,"All Patients");
String AllPatients="select * from tblPtDetails";
JTable tablePatientDt = new JTable(Bquery.buildTableModel(Bquery.resultQuery(AllPatients)));
tablePatientDt.setEnabled(false);
tablePatientDt.setPreferredScrollableViewportSize(new Dimension(1200, 400));
tablePatientDt.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
tablePatientDt.setRowHeight(30);
tablePatientDt.setAutoCreateRowSorter(true);
card3.add(tablePatientDt);
card3.add(new JScrollPane(tablePatientDt), BorderLayout.CENTER);
for (int i = 0; i < (tablePatientDt.getColumnCount()); i++) {
TableColumn columnPatients = null;
columnPatients = tablePatientDt.getColumnModel().getColumn(i);
columnPatients.setPreferredWidth(70); //sport column is bigger
}
tablePatientDt.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int row = tablePatientDt.getSelectedRow();
int column = tablePatientDt.getSelectedColumn();
//Object val= tablePatientDt.getModel().getValueAt(row, column);
//tablePatientDt.getModel().getValueAt(row, column);
//return tablePatientDt.getModel().getValueAt(row, column);
System.out.println(row);
JFrame newFrame = new JFrame();
newFrame.setTitle("Detail Screen");
newFrame.setVisible(true);
}
}
});
Your main problem is here:
tablePatientDt.setEnabled(false);
Because the table is not enabled, no cell or row can ever be selected, and so the selected row will always be -1. Solution: get rid of that line. Instead, if you don't want a cell to be editable on double click, override the JTable or its model and override the isCellEditable method:
e.g.,
// create your JTable model here:
DefaultTableModel model = ......
JTable tablePatientDt = new JTable(model){
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
Other issues: don't add the JTable to more than one component as you're doing. That spells great risk for trouble since Swing components can be added to only one component at a time.
A side recommendation: in your future questions post only small compilable and runnable programs. Your code above cannot run since it has database dependencies that we don't have access to, and is also incomplete. In order to find your problem, I had to take your code and create a small runnable program with it, an mcve (please read the link):
import java.awt.*;
import java.awt.event.*;
import java.sql.SQLException;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
public class CardLayoutExample {
private static JScrollPane scrollPane;
public static void main(String[] arguments) throws SQLException {
// main window
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame window = new JFrame("CardLayout Example");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// !! window.setSize(1500, 800);
window.getContentPane().setLayout(new BorderLayout());
final CardLayout cardLayout = new CardLayout();
final JPanel cardPanel = new JPanel(cardLayout);
JPanel card3 = new JPanel();
cardPanel.add(card3, "All Patients");
String AllPatients = "select * from tblPtDetails";
//!!
String[][] data = {{"1", "2", "3"}, {"4", "5", "6"}, {"7", "8", "9"}};
String[] columnNames = {"One", "Two", "Three"};
DefaultTableModel model = new DefaultTableModel(data, columnNames);
// !! JTable tablePatientDt = new JTable(Bquery.buildTableModel(Bquery.resultQuery(AllPatients)));
JTable tablePatientDt = new JTable(model){
#Override
public boolean isCellEditable(int row, int column) {
return false;
}
};
// !! tablePatientDt.setEnabled(false);
tablePatientDt.setPreferredScrollableViewportSize(new Dimension(1200, 400));
tablePatientDt.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tablePatientDt.setRowHeight(30);
tablePatientDt.setAutoCreateRowSorter(true);
// !! card3.add(tablePatientDt);
card3.add(new JScrollPane(tablePatientDt), BorderLayout.CENTER);
for (int i = 0; i < (tablePatientDt.getColumnCount()); i++) {
TableColumn columnPatients = null;
columnPatients = tablePatientDt.getColumnModel().getColumn(i);
columnPatients.setPreferredWidth(70); // sport column is bigger
}
tablePatientDt.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int row = tablePatientDt.getSelectedRow();
int column = tablePatientDt.getSelectedColumn();
// Object val= tablePatientDt.getModel().getValueAt(row,
// column);
// tablePatientDt.getModel().getValueAt(row, column);
// return tablePatientDt.getModel().getValueAt(row, column);
System.out.println(row);
JFrame newFrame = new JFrame();
newFrame.setTitle("Detail Screen");
newFrame.setVisible(true);
}
}
});
//!!
window.add(cardPanel);
window.pack();
window.setVisible(true);
}
}
But really this effort should be yours not mine, since we're all volunteers, and you're the one asking for volunteer help in solving a problem. So in the future we ask that you create your own mcve to go with your questions.
Also that detail window shouldn't be a JFrame but rather a JDialog.
In the example, I'm seeking to add a table to my GUI and then dynamically add rows to it (to show the progress). What I don't understand is why all the rows are appearing at once. I mean, the the table's changing, isn't it? Can someone please give me an explanation?
import java.awt.Component;
public class Main {
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
GUI gui = new GUI();
gui.setVisible(true);
DefaultTableModel model = new DefaultTableModel(
new String[] { "Column 1", "Column 2" }, 0);
JTable table = new JTable(model);
gui.add(table);
gui.validate();
for (int i = 0; i < 10; i++) {
System.out.println("Row " + i);
model
.addRow(new String[] { "Row", String.valueOf(i) });
// model.fireTableDataChanged();
try {
Thread.sleep(250);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}
class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 100);
setLocationRelativeTo(null);
JPanel cp = new JPanel();
cp.setBorder(new EmptyBorder(10, 10, 10, 10));
setContentPane(cp);
}
}
Reiterating Kleopatra : Don't Sleep the EDT
You can instead use a javax.swing.Timer as seen in this answer
EDIT
I didn't want to mess with your code too much (just because it looks weird to me) but I changed it somewhat to add the Timer
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
public class Main {
static JTable table;
static GUI gui;
static Processor p = null;
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
gui = new GUI();
p = new Processor() {
#Override
public void execute() {
final JTable table = new JTable(p.getTableModel());
final JScrollPane scrollPane = new JScrollPane(table);
gui.getContentPane().add(scrollPane, BorderLayout.CENTER);
gui.setLocationRelativeTo(null);
gui.setVisible(true);
Timer timer = new Timer(100, new ActionListener(){
public void actionPerformed(ActionEvent e) {
p.processRow();
table.scrollRectToVisible(table.getCellRect(table.getRowCount() - 1, 0, true));
}
});
timer.start();
}
};
p.execute();
}
});
}
}
class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 400);
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(10, 10, 10, 10));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
interface Callback {
void execute();
}
abstract class Processor implements Callback {
private final String[] cols = {"COL", "COL", "COL", "COL", "COL"};
private DefaultTableModel tableModel;
int numRows;
int numCols;
int a, b, c, d, e;
Processor() {
a = 1; b = 2; c = 3; d = 4; e = 4;
numRows = 1000;
tableModel = new DefaultTableModel(cols, numCols);
}
public DefaultTableModel getTableModel() {
return tableModel;
}
public void processRow() {
tableModel.addRow(new Object[]{a, b, c, d, e});
a++; b++; c++; d++; e++;
}
}
As pointed out by kleopatra and peeskillet, my initial example suffered from a stupid mistake. It's worth noting that peeskillet and I were following different approaches, though. In my example, the columns meant to represent connection attempts (more or less) that can take an unknown amount of time and that can actually fail (in that case, and only in that case, the next column would come into play and so on). Therefore, it wouldn't have made sense for me to add the rows at once (which was probably what made my example look weird to peeskillet). I've solved the task using a SwingWorker. As pointed out by kleopatra, there was a another mistake, which is now fixed. Here's my code:
package SwingWorkerExampleCopy;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import java.awt.BorderLayout;
public class SwingWorkerExampleCopy {
public static void main(String[] args) {
// Show GUI
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GUI gui = new GUI();
DefaultTableModel tableModel = new DefaultTableModel();
// Use a SwingWorker
Worker worker = new Worker(tableModel);
worker.execute();
JTable table = new JTable(tableModel);
table.setEnabled(false);
// table.setTableHeader(null);
JScrollPane scrollPane = new JScrollPane(table);
gui.getContentPane()
.add(scrollPane, BorderLayout.CENTER);
}
});
}
}
class GUI extends JFrame {
private static final long serialVersionUID = 1L;
public GUI() {
setTitle("GUI");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 350, 400);
setLocationRelativeTo(null);
setVisible(true);
JPanel contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(10, 10, 10, 10));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
}
}
class Worker extends SwingWorker<DefaultTableModel, Object[]> {
private final static int numRows = 10;
private final static int numCols = 10;
private DefaultTableModel model;
Worker(DefaultTableModel model) {
this.model = model;
model.setColumnCount(numCols);
}
#Override
protected DefaultTableModel doInBackground() throws Exception {
// Add row
for (int row = 0; row < numRows; row++) {
// Build columns
for (int col = 0; col < numCols; col++) {
if (col == 0) {
publish(new Object[] { new String("Row " + row), row,
col });
} else {
// Simulate a slow source
Thread
.sleep(new Random().nextInt((250 - 50) + 1) + 50);
Boolean isSuccessful = false;
// Simulate a return value
if (new Random().nextBoolean()) {
isSuccessful = true;
}
publish(new Object[] {
new String((isSuccessful == true ? "x" : "o")), row,
col });
if (isSuccessful == true) {
break;
}
}
}
}
return model;
}
#Override
protected void process(List<Object[]> chunks) {
for (Object[] chunk : chunks) {
// chunk[0]: cell value
// chunk[1]: number
// chunk[2]: column
if ((int) chunk[2] == 0) {
Object[] row = new Object[numCols];
row[0] = (Object) chunk[0];
model.addRow(row);
} else {
model.setValueAt((Object) chunk[0], (int) chunk[1],
(int) chunk[2]);
}
}
}
}
Because while your code is running, no other events (such as repaint events) can execute - you're blocking the event thread until you're done.
You may be able to call repaint directly, but the UI will still be unresponsive to input while your code is running. You'd be better off running the loop in a separate worker thread, and using invokeLater or invokeAndWait to perform the updates to the UI when needed.
I have written this simple program:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class JcomboboxJtableDemo extends JPanel
implements ActionListener {
private DefaultTableModel tableModel;
JTable table = new JTable (tableModel);
private JScrollPane scrollpaneTable = new JScrollPane( table );
private JPanel PaneBottoniTabella = new JPanel( );
public JcomboboxJtableDemo() {
super(new BorderLayout());
String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
JComboBox comboBox = new JComboBox(petStrings);
comboBox.setSelectedIndex(4);
tableModel = CreateTableModel();
tableModel.insertRow( 0, new Object[] {"Header col1", ""} );
tableModel.insertRow( 0, new Object[] {petStrings[0], ""} );
tableModel.insertRow( 0, new Object[] {petStrings[1], ""} );
tableModel.insertRow( 0, new Object[] {petStrings[2], ""} );
tableModel.insertRow( 0, new Object[] {petStrings[3], ""} );
tableModel.setValueAt("Header col2", 0, 1);
DefaultCellEditor editor = new DefaultCellEditor(comboBox);
table.getColumnModel().getColumn(0).setCellEditor(editor);
table.getColumnModel().getColumn(1).setCellEditor(editor);
//Lay out the demo.
add(comboBox, BorderLayout.PAGE_START);
add(table, BorderLayout.PAGE_END);
setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
}
private final DefaultTableModel CreateTableModel () {
DefaultTableModel modello = new DefaultTableModel( new Object[] { "Col1","Col2" }, 0 ) {
#Override
public boolean isCellEditable(int row, int column) {
return true;
}
};
table.setModel(modello);
return modello;
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("ComboBoxDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new JcomboboxJtableDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I you try to run it you will see that there is a problem in showing correctly the JComboBox components in the second column, in the first column the are correctly shown and you can see each selected item as set in the code, instead in the second column there are some problems: none on the relative cell.
Could you tell me why? How can I solve the problem?
Thanks
You're using the same JComboBox component for both ColumnModel columns which in turn share the same ComboBoxModel. Any change in the selected item from one column will be reflected in the other column. Create a second combobox
JComboBox comboBox2 = new JComboBox(petStrings);
...
table.getColumnModel().getColumn(1).setCellEditor(editor2);
so that any changes can occur independently in either column.
For example I have a Table that I want to get text that's in the first column and store it in an ArrayList.
Java Tables often use the TableModel interface for storage.
You can get the a particular value via:
myJTable.getModel().getValueAt(rowIndex, columnIndex);
More on that: Sun's Swing Table Tutorial
public static void main(String[] args)
{
try
{
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container cp = frame.getContentPane();
cp.setLayout(new FlowLayout());
final JTable tbl = new JTable(new String[][]{{"c1r1", "c2r1"}, {"c1r2", "c2r2"}}, new String[]{"col 1", "col 2"});
cp.add(tbl);
cp.add(new JButton(new AbstractAction("click")
{
#Override
public void actionPerformed(ActionEvent e)
{
List<String> colValues = new ArrayList<String>();
for (int i = 0; i < tbl.getRowCount(); i++)
colValues.add((String) tbl.getValueAt(0, i));
JOptionPane.showMessageDialog(frame, colValues.toString());
}
}));
frame.pack();
frame.setVisible(true);
}
catch (Throwable e)
{
e.printStackTrace();
}
}
You need to go through the JTable's TableModel, accessible through the getModel() method. That has all the information you need.