I have been searching for a solution to be able to increase the height of a row in a JTable. I have been using the setRowHeight(int int) method which compiles and runs OK, but no row[s] have been increased. When I use the getRowHeight(int) method of the row I set the height to, it does print out the size I increased the row to, so I'm not sure what is wrong. The code below is a rough illustration how I am trying to solve it.
My class extends JFrame.
String[] columnNames = {"Column 1", "Column 2", "Column 1 3"};
JTable table = new JTable(new DefaultTableModel(columnNames, people.size()));
DefaultTableModel model = (DefaultTableModel) table.getModel();
int count =1;
for(Person p: people)
{
model.insertRow(count,(new Object[]{count, p.getName(), p.getAge()+"",
p.getNationality}));
count++;
}
table.setRowHeight(1, 15);//Try set height to 15 (I've tried higher)
Can anyone tell me where I am going wrong? I am trying to increase the height of row 1 to 15 pixels?
You can use:
table.setRowHeight(int par1);
or if you wanted to set the row height for a specific row, use:
table.setRowHeight(int par1, int par2);
Not sure what is the intention of leaving the first row at index 0 empty. Rows in JTable run from index 0. It is best if you could post a complete example (ie SSCCE) that demonstrates the issues. Compare to this simple example that works OK:
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();
}
});
}
}
Right click on the JTable in JFrame and click Properties.
Scroll down and set the rowHeight value.
You can also add a tableModelListener?
model.addTableModelListener(new TableModelListener() {
#Override public void tableChanged(final TableModelEvent e) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
table.setRowHeight(e.getFirstRow(), 15); //replace 15 with your own height
}
});
}
});
Related
I have two JTables one in main viewport and one in footer viewport, using JideScrollPane.
the problem is when the main JTable's view is customized, the footer JTable remains the
same, is there any way to synchronize their view?
thanks.
EDIT:
Here's a demo that will synch up the resizing of two tables that have similar columns. The idea is:
Create a custom TableColumnModelListener for each table's column model.
Upon resize, sync up the column widths. You'll have to disable the other listener temporarily, while this is happening.
For moving of columns, implement that logic in columnMoved(...) [left as an exercise]
This shows two-way synching:
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class JTableResizeColumnsDemo implements Runnable
{
JTable table1, table2;
TableColumnModelListener columnListener1, columnListener2;
Map<JTable, TableColumnModelListener> map;
public static void main(String[] args)
{
SwingUtilities.invokeLater(new JTableResizeColumnsDemo());
}
public void run()
{
Vector<String> names = new Vector<String>();
names.add("One");
names.add("Two");
names.add("Three");
table1 = new JTable(null, names);
table2 = new JTable(null, names);
columnListener1 = new ColumnChangeListener(table1, table2);
columnListener2 = new ColumnChangeListener(table2, table1);
table1.getColumnModel().addColumnModelListener(columnListener1);
table2.getColumnModel().addColumnModelListener(columnListener2);
map = new HashMap<JTable, TableColumnModelListener>();
map.put(table1, columnListener1);
map.put(table2, columnListener2);
JPanel p = new JPanel(new GridLayout(2,1));
p.add(new JScrollPane(table1));
p.add(new JScrollPane(table2));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(p);
frame.setSize(300, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
class ColumnChangeListener implements TableColumnModelListener
{
JTable sourceTable;
JTable targetTable;
public ColumnChangeListener(JTable source, JTable target)
{
this.sourceTable = source;
this.targetTable = target;
}
public void columnAdded(TableColumnModelEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
public void columnRemoved(TableColumnModelEvent e) {}
public void columnMoved(TableColumnModelEvent e) {}
public void columnMarginChanged(ChangeEvent e)
{
TableColumnModel sourceModel = sourceTable.getColumnModel();
TableColumnModel targetModel = targetTable.getColumnModel();
TableColumnModelListener listener = map.get(targetTable);
targetModel.removeColumnModelListener(listener);
for (int i = 0; i < sourceModel.getColumnCount(); i++)
{
targetModel.getColumn(i).setPreferredWidth(sourceModel.getColumn(i).getWidth());
}
targetModel.addColumnModelListener(listener);
}
}
}
You can apply an Observer pattern: the first JTable observes the second and vice versa. Then you add listners to both tables so that, when one is "customized", the other is notified. Basically, "being notified" consists in a method invocation that causes the update of the JTable.
In order to do that, you have two options:
You define a class Observer with a "register" method and a
"notify" method. When creating a JTable, you register it with the
Observer. Then, the listener you create and associate to each JTable
invoke the "notify" method of the observer, which informs all other
registered JTables of the change
You define a sort of "callback method" notify in the class that contains and declares the JTable. This "notify" method is invoked within the listner and updates the correct JTable. You can also create two methods: one for updating one JTable and one for the other JTable
Usually this is done by using the same model for different ui components. Sadly the JTable contains a bug that will cause problems when sharing the TableColumnModel.
But you can work around it using this JTable
class ShareableColumnModelTable extends JTable {
/**
* Fixes http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4816146 and
* more...
*
*/
#Override
public void columnMarginChanged(ChangeEvent e) {
if (isEditing()) {
removeEditor();
}
TableColumn resizingColumn = null;
if (tableHeader != null) {
resizingColumn = tableHeader.getResizingColumn();
}
if (resizingColumn != null) {
if (autoResizeMode == AUTO_RESIZE_OFF) {
resizingColumn.setPreferredWidth(resizingColumn.getWidth());
} else { // this else block is missing in jdk1.4 as compared to
// 1.3
TableColumnModel columnModel = getColumnModel();
/**
* Temporarily disconnects this column listener to prevent
* stackoverflows if the column model is shared between
* multiple JTables.
*/
columnModel.removeColumnModelListener(this);
try {
doLayout();
} finally {
columnModel.addColumnModelListener(this);
}
repaint();
return;
}
}
resizeAndRepaint();
}
}
With the ShareableColumnModelTableshowed above you can share one column model bettween multiple tables.
public static void main(String[] args) {
JFrame frame = new JFrame("Column Sync");
Container contentPane = frame.getContentPane();
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setResizeWeight(0.5d);
contentPane.add(splitPane);
JTable table1 = new ShareableColumnModelTable();
JTable table2 = new ShareableColumnModelTable();
TableColumnModel tableColumnModel = createTableColumnModel();
table1.setModel(createTableModel1());
table2.setModel(createTableModel2());
table1.setColumnModel(tableColumnModel);
table2.setColumnModel(tableColumnModel);
splitPane.setLeftComponent(new JScrollPane(table1));
splitPane.setRightComponent(new JScrollPane(table2));
showFrame(frame);
}
private static TableColumnModel createTableColumnModel() {
TableColumnModel tableColumnModel = new DefaultTableColumnModel();
TableColumn column1 = new TableColumn(0);
column1.setHeaderValue("1. column");
tableColumnModel.addColumn(column1);
TableColumn column2 = new TableColumn(1);
column2.setHeaderValue("2. column");
tableColumnModel.addColumn(column2);
return tableColumnModel;
}
private static TableModel createTableModel1() {
DefaultTableModel tableModel = new DefaultTableModel();
tableModel.setColumnCount(2);
tableModel.addRow(new Object[] { "a", "b" });
return tableModel;
}
private static TableModel createTableModel2() {
DefaultTableModel tableModel = new DefaultTableModel();
tableModel.setColumnCount(2);
tableModel.addRow(new Object[] { "c", "d" });
return tableModel;
}
private static void showFrame(JFrame frame) {
frame.setSize(240, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
I have a JTable with the column names " Names " , " Quantity " and " Unit " .
I'm coding a program where you get the ingredients names.
So i need to get the whole row of one column and String it all up together,
because i need to store it in mySql and i have already set it all as String.
Any idea how i can do this ?
My code are as follows :
JTable Code:
DefaultTableModel model = (DefaultTableModel)table.getModel();
if(!txtQty.getText().trim().equals("")){
model.addRow(new Object[]{ingCB.getSelectedItem().toString(),txtQty.getText(),unitCB.getSelectedItem().toString()});
}else{
JOptionPane.showMessageDialog(null,"*Quantity field left blank");
}
Getting the values and for storing :
for(int i = 1; i<= i ; i++){
ingredients = table.getName();
}
This is for loop is wrong and it does not work because i have a constructor to take in Ingredients but because it is inside the loop, it cannot take it in.
Any suggestions please ? Thank you.
Constructor :
Food e2 = new Food(Name, Description, priceDbl, Image, Category, Ingredients, promotion );
e2.createFood();
I'm coding a program where you get the ingredients names. So i need to get the whole row of one column and String it all up together, because i need to store it in mySql and i have already set it all as String.
Want to do so, try this. Here I am getting result into ArrayList and String, as I am commented ArrayList you can avoid it.
public class TableValuePrint extends JFrame implements ActionListener{
private final JButton print;
private final JTable table;
private String str="";
public TableValuePrint() {
setSize(600, 300);
String[] columnNames = {"A", "B", "C"};
Object[][] data = {
{"Moni", "adsad", "Pass"},
{"Jhon", "ewrewr", "Fail"},
{"Max", "zxczxc", "Pass"}
};
table = new JTable(data, columnNames);
JScrollPane tableSP = new JScrollPane(table);
JPanel tablePanel = new JPanel();
tablePanel.add(tableSP);
tablePanel.setBackground(Color.red);
add(tablePanel);
setTitle("Result");
setSize(1000,700);
print=new JButton("Print");
JPanel jpi1 = new JPanel();
jpi1.add(print);
tablePanel.add(jpi1,BorderLayout.SOUTH);
print.addActionListener(this);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
TableValuePrint ex = new TableValuePrint();
ex.setVisible(true);
}
});
}
#Override
public void actionPerformed(ActionEvent ae) {
if(ae.getSource()==print){
// ArrayList list = new ArrayList();
for(int i = 0;i<table.getModel().getRowCount();i++)
{
//list.add(table.getModel().getValueAt(i, 0)); //get the all row values at column index 1
str=str+table.getModel().getValueAt(i,0).toString();
}
//System.out.println("List="+list);
System.out.println("String="+str);
}
}
}
Output
String=MoniJhonMax
I need to create a JTable inside JScrollPane with resizeable columns (when user increase column width - horizontal scrollbar appears).
For this I have use table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);.
Also, when viewport is wide enough to contain entire table - columns should stretch to fill viewport width.
To accomplish this I have override getScrollableTracksViewportWidth() method of JTable class as follow:
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
This approach works good, except one thing: when I first time try to resize column it return own width to start position. If I quickly resize column and release mouse table continue to work good.
So, what is the reason of such behavior? Why table try to resize even if getScrollableTracksViewportWidth() returns false? Or, maybe, you can propose better solution for implementing such resize mode?
Bellow is a simple working example of above problem:
import javax.swing.*;
public class TestTable {
private static Object[][] data = new Object[][] {
{ "a", "b", "c" },
{ "d", "e", "f" }
};
private static Object[] colNames = new Object[] { "1", "2", "3" };
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JTable table = new JTable(data, colNames) {
#Override
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setVisible(true);
}
});
}
}
It seemed like the default doLayout() logic wasn't working when you tried to increase the size of a column when the horizontal scrollbar wasn't visible, so I got rid of the default logic and just accepted the width of the column without attempting to adjust it.
import javax.swing.*;
import javax.swing.table.*;
public class TestTable {
private static Object[][] data = new Object[][] {
{ "a", "b", "c" },
{ "d", "e", "f" }
};
private static Object[] colNames = new Object[] { "1", "2", "3" };
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JTable table = new JTable(data, colNames)
{
#Override
public boolean getScrollableTracksViewportWidth()
{
return getPreferredSize().width < getParent().getWidth();
}
#Override
public void doLayout()
{
TableColumn resizingColumn = null;
if (tableHeader != null)
resizingColumn = tableHeader.getResizingColumn();
// Viewport size changed. May need to increase columns widths
if (resizingColumn == null)
{
setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
super.doLayout();
}
// Specific column resized. Reset preferred widths
else
{
TableColumnModel tcm = getColumnModel();
for (int i = 0; i < tcm.getColumnCount(); i++)
{
TableColumn tc = tcm.getColumn(i);
tc.setPreferredWidth( tc.getWidth() );
}
// Columns don't fill the viewport, invoke default layout
if (tcm.getTotalColumnWidth() < getParent().getWidth())
setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
super.doLayout();
}
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
};
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setVisible(true);
}
});
}
}
Edited to use AUTO_RESIZE_ALL_COLUMNS.
I have a multi threaded program and I'm trying to use a JTable to view the progress of each thread. Each thread is a row the JTable. I have posted a simplified and relevant code of what I have now. Each time I press start a new thread is started and the row is added to the JTable. But how do I update the "Column 2" which of the row which belongs to that thread from the running thread?
In main this is what I have
JTable table = new JTable(new DefaultTableModel(new Object[]{"Thread Name", "Progress"}, 0));
btnBegin.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
Thread newThread = new Thread(new MyThreadClass(country, category));
newThread.start();
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.addRow(new Object[]{"Thread " + threadNumber, "Column 2"});
}
});
If you can add a field tableModel to MyThreadClass (field+constructor argument), from this class, you can invoke the code #sedran was mentioning.
Example:
public class T {
static int threadNumber = 0;
public static void main(String[] args) throws Exception {
final JTable table = new JTable(new DefaultTableModel(new Object[] { "Thread Name", "Progress" }, 0));
JButton btnBegin = new JButton("Begin");
btnBegin.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
MyThreadClass newThread = new MyThreadClass(model, threadNumber++);
newThread.start();
model.addRow(new Object[] { "Thread " + threadNumber, "Column 2" });
}
});
JFrame frame = new JFrame();
frame.add(btnBegin, BorderLayout.NORTH);
frame.add(table);
frame.setSize(400, 400);
frame.setVisible(true);
}
static class MyThreadClass extends Thread {
private final DefaultTableModel model;
private final int threadNumber;
public MyThreadClass(DefaultTableModel model, int threadNumber) {
super();
this.model = model;
this.threadNumber = threadNumber;
}
#Override
public void run() {
for (int i = 0; i <= 5; i++) {
final int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
model.setValueAt(index * 20, threadNumber, 1);
}
});
}
}
}
}
Please note that the use of SwingUtilities.invokeLater is important as the view MUST be refreshed in the EDT.
if you want to update the column at runtime and if you know in which row you want to update, you can use it this way:
int row; // row number you want to update.
int column = 1; // You want to update the first row.
DefaultTableModel model = (DefaultTableModel)table.getModel();
Object value = "New Value Of This Cell";
model.setValueAt(value, row, column);
You can pass or set your table, and the row in which the thread is showing to the MyThreadClass, so it can update its row by itself.
You can write a method insie MyThreadClass like this:
public void setTableAndRow(int row, JTable table) {
this.table = table;
this.row = row;
}
You can pass those arguments while creating the thread:
btnBegin.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
MyThreadClass myThread = new MyThreadClass(country, category);
Thread newThread = new Thread(myThread);
/**
* We know, if there is n rows starting from 0,
* index of the row that we're adding is n.
*/
int row = model.getRowCount();
myThread.setTableAndRow(row, table);
// Now we add the row before starting the thread, because it
// will try to reach that row, we don't want any exceptions.
model.addRow(new Object[]{"Thread " + threadNumber, "Column 2"});
newThread.start();
}
});
Suggestions:
Use a SwingWorker object to create your background thread.
One way to get updates from the SwingWorker is by using its publish/process method pair. This allows the background thread to pass data to the Swing application on the Swing event thread. This will allow the background process to "push" data onto the GUI.
The other way to get updates is to add a PropertyChangeListener to the SwingWorker and have the SwingWorker update bound properties. Either will work fine. This will allow data to be "pulled" from the background process onto the GUI.
To update the data in a specific data row, you'll need to somehow connect a row of the model with the thread you're following. You could always iterate through the cells of the a certain column of the table model using getValueAt(...) until you've found one that contains data that matches that of the thread, perhaps a field of the watched class. Then you can update the data held by a different column of that row using JTable's setValueAt(...)
I think SwingUtilies.invokeLater() is what you are looking for, so you can access event dispatch thread.
This is the first time for me to post here, so sorry if I made some mistake.
I am working on a JTable which column data have to verify some parameters, for example:
Column 3 values > 30
Column 4 values > 10
Column 5 values > 4
Also the first 2 columns are filled "automatically", putting 0s in the rest of the columns.
If that data is correct, in the Column 5 I would show an image of a tick, otherwise, I would show an image of a warning.
For verifying this I use the following code
ImageIcon accept = new javax.swing.ImageIcon(getClass().getResource("/resources/accept.png"));
ImageIcon deny = new javax.swing.ImageIcon(getClass().getResource("/resources/exclamation.png"));
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
double d1 = Double.valueOf(jTable.getValueAt(row, 2).toString());
double d2 = Double.valueOf(jT.getValueAt(row, 3).toString());
double d3 = Double.valueOf(jT.getValueAt(row, 4).toString());
if(d1>MAX_A||d2>MAX_B||d3>MAX_C){
jTable.setValueAt(deny, row, 5);
}
else{
jTable.setValueAt(accept, row, 5);
}
}
The problem of this code is that returns a Stack Overflow, and I don't know how to handle this.
Is there any other way to implement some verifier on a table that implies multiple cells?
Thanks in advance.
The problem of this code is that
returns a Stack Overflow, and I don't
know how to handle this.
The problem is that your code sets a value in the model listener so another tableChanged event is generated. Your code should be something like:
if (e.getColumn() != 5)
// do your code
I don't see a problem using a TableModelListener to dynamically set the value of a column based on data in another column. Here is a simple example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class TableProcessing extends JPanel implements TableModelListener
{
public TableProcessing()
{
String[] columnNames = {"Item", "Quantity", "Price", "Cost"};
Object[][] data =
{
{"Bread", new Integer(1), new Double(1.11), new Double(1.11)},
{"Milk", new Integer(1), new Double(2.22), new Double(2.22)},
{"Tea", new Integer(1), new Double(3.33), new Double(3.33)},
{"Cofee", new Integer(1), new Double(4.44), new Double(4.44)}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
// Returning the Class of each column will allow different
// renderers to be used based on Class
#Override
public Class getColumnClass(int column)
{
return getValueAt(0, column).getClass();
}
// The Cost is not editable
#Override
public boolean isCellEditable(int row, int column)
{
return (column == 3) ? false : true;
}
};
model.addTableModelListener( this );
JTable table = new JTable( model );
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane( table );
add( scrollPane );
String[] items = { "Bread", "Milk", "Tea", "Coffee" };
JComboBox<String> editor = new JComboBox<String>( items );
DefaultCellEditor dce = new DefaultCellEditor( editor );
table.getColumnModel().getColumn(0).setCellEditor(dce);
}
/*
* The cost is recalculated whenever the quantity or price is changed
*/
public void tableChanged(TableModelEvent e)
{
if (e.getType() == TableModelEvent.UPDATE)
{
int row = e.getFirstRow();
int column = e.getColumn();
if (column == 1 || column == 2)
{
TableModel model = (TableModel)e.getSource();
int quantity = ((Integer)model.getValueAt(row, 1)).intValue();
double price = ((Double)model.getValueAt(row, 2)).doubleValue();
Double value = new Double(quantity * price);
model.setValueAt(value, row, 3);
}
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Table Model Listener");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TableProcessing());
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
You probably get your error because of getFirstRow call. I think it's a bad idea to validate table structure in JTable.tableChanged - how do you know that the table was fully filled and ready for validation? I would suggest filling the whole table first, then invokation of validation. Maybe there would be also a good idea to use separate table to display validation results