I have a problem and really don't know how to solve it.
I used some solutions from this forum but they don't work.
This is the piece of code:
package own_components.custom_components;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import own_components.localizable_components.LocalizableComponent;
import localization.GUILocalizationTags;
import localization.LocalizationManager;
public class OutputJTable extends JTable implements CustomComponent
{
private CustomTableModel dataModel = new CustomTableModel();
private List<String[]> data = new ArrayList<String[]>();
private final int COLUMNS_AMOUNT = 3;
private final int _1ST_COL_WIDTH = 25;
private final int _2ST_COL_WIDTH = 45;
private final int _3ND_COL_WIDTH = 505;
public OutputJTable()
{
setModel(dataModel);
setTableProperties();
dataModel.addTableModelListener(new TableModelListener(){
#Override
public void tableChanged(TableModelEvent paramTableModelEvent)
{
fitRowsHeight();
}
});
}
private void setTableProperties()
{
//some properties of table
}
public void setResultOutput(List<String[]> result)
{
data = new ArrayList<String[]>();
data.add(new String[] { "l", "code", "222222222222222222222222222 22ddddddddddddddddddddddddddd22222222222222222222222222222222" });
data.add(new String[] { "l", "code", "sssssssssssssssssssssssssssssss sssssssssssssssssssssssssssssssssssssssssssssssssss222222222" });
dataModel.fireTableDataChanged();
}
private void fitRowsHeight()
{
for (int row = 0; row < getRowCount(); row++)
{
int rowHeight = getRowHeight();
Component comp = prepareRenderer(getCellRenderer(row, 2), row, 2);
rowHeight = Math.max(rowHeight, comp.getSize().height);
setRowHeight(row, rowHeight);
}
}
public int getSelectedRow()
{
return selectedRow;
}
private class CustomTableModel extends AbstractTableModel implements LocalizableComponent
{
private static final long serialVersionUID = -992340559233338699L;
private String[] columnsNames = { "a", "b", "c" };
#Override
public String getColumnName(int paramInt)
{
return columnsNames[paramInt];
}
#Override
public boolean isCellEditable(int paramInt1, int paramInt2)
{
return false;
}
#Override
public int getColumnCount()
{
return COLUMNS_AMOUNT;
}
#Override
public int getRowCount()
{
return data.size();
}
#Override
public String getValueAt(int arg0, int arg1)
{
return data.get(arg0)[arg1];
}
#Override
public void useTranslatedText(String tag)
{
columnsNames[1] = tag;
getColumnModel().getColumn(2).setHeaderValue(tag);
repaint();
}
#Override
public void registerToLocalization(LocalizationManager lm, String key)
{
lm.registerToTranslationList(this, GUILocalizationTags.OUT_TAB_DESCRIPTION);
}
}
private class CustomTableRenderer extends DefaultTableCellRenderer
{
JTextArea cellTemp = new JTextArea();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
cellTemp = new JTextArea(data.get(row)[column]);
cellTemp.setLineWrap(true);
return cellTemp;
}
}
}
This is little bit long but rather simple: my table uses custom cell renderer which contains JTextArea. I use JTA because I need Strings wrapping. After put such JTextAreas I expect to set row heights to highest JTA in a row.
And here is the problem. In code above I expect to receive JTA.height but I still receive "0". The same situation with JTA.getRows().
I really don't understand why. Can anybody explain me what is wrong with this code?
This is working JTable with wrapped strings.
(I used solutions introduced by mKorbel in this thread How to make a JTable column to contain not JTextFields, but JTextAreas)
package own_components.custom_components;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.View;
public class OutputJTable extends JTable
{
private static final long serialVersionUID = 1L;
private List<String[]> data = new ArrayList<String[]>();
private CustomTableModel dataModel = new CustomTableModel();
private final int COLUMNS_AMOUNT = 3;
private final int _1ST_COL_WIDTH = 25;
private final int _2ST_COL_WIDTH = 45;
private final int _3ND_COL_WIDTH = 505;
private int selectedRow = -1;
public OutputJTable()
{
setModel(dataModel);
setDefaultRenderer(Object.class, new CustomTableRenderer());
setTableProperties();
}
/**
* Sets basic table properties.
*/
private void setTableProperties()
{
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
getColumnModel().getColumn(0).setMaxWidth(_1ST_COL_WIDTH);
getColumnModel().getColumn(0).setMinWidth(_1ST_COL_WIDTH);
getColumnModel().getColumn(1).setMaxWidth(_2ST_COL_WIDTH);
getColumnModel().getColumn(1).setMinWidth(_2ST_COL_WIDTH);
getColumnModel().getColumn(2).setMaxWidth(_3ND_COL_WIDTH);
getColumnModel().getColumn(2).setMinWidth(_3ND_COL_WIDTH);
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setIntercellSpacing(new Dimension(0, 0));
setShowGrid(false);
}
/**
* Receives data used to modified data showned in table.
* This should be only access point to add data used by data model (which is used by jtable).
*
* #param result
*/
public void setResultOutput(List<String[]> result)
{
data = new ArrayList<String[]>();
data = result;
dataModel.fireTableDataChanged();
}
#Override
public void doLayout()
{
super.doLayout();
for (int row = 0; row < getRowCount(); row++)
{
JTextArea a = (JTextArea) prepareRenderer(getDefaultRenderer(Object.class), row, 2);
int rowHeight = (int) a.getUI().getRootView(a).getView(0).getPreferredSpan(View.Y_AXIS) + getIntercellSpacing().height;
setRowHeight(row, rowHeight);
}
}
/**
* Returns which row is selected. Main purpose of this method is provide data to PrintManager what should be printed.
*/
public int getSelectedRow()
{
return selectedRow;
}
#Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
{
if (rowIndex != selectedRow)
{
selectedRow = rowIndex;
}
else
{
selectedRow = -1;
}
super.changeSelection(rowIndex, columnIndex, true, false);
}
/**
* This is model used to fill this table with data.
*/
private class CustomTableModel extends DefaultTableModel implements LocalizableComponent
{
private static final long serialVersionUID = -992340559233338699L;
private String[] columnsNames = { "a", "b", "c" };
#Override
public String getColumnName(int paramInt)
{
return columnsNames[paramInt];
}
#Override
public boolean isCellEditable(int paramInt1, int paramInt2)
{
return false;
}
#Override
public int getColumnCount()
{
return COLUMNS_AMOUNT;
}
#Override
public int getRowCount()
{
return data.size();
}
#Override
public String getValueAt(int arg0, int arg1)
{
return data.get(arg0)[arg1];
}
}
/**
* This class is used to render single cell.
*/
private class CustomTableRenderer extends JTextArea implements TableCellRenderer
{
private final Color SELECTION_BORDER = new Color(200, 200, 200);
private final Color ODD_BACKGR_COLOR = new Color(240, 240, 240);
private final Color EVEN_BACKGR_COLOR = Color.WHITE;
CustomTableRenderer()
{
setLineWrap(true);
setWrapStyleWord(true);
setEditable(false);
setFont(getFont());
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
setText((String) value);
setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
if (isSelected)
{
setBackground(SELECTION_BORDER);
}
else if (row % 2 != 0)
{
setBackground(ODD_BACKGR_COLOR);
}
else
{
setBackground(EVEN_BACKGR_COLOR);
}
return this;
}
}
}
Remarks:
formating of row height is based on third column, if you want to take under consideration all columns, you have to use additional "for" loop in doLayout(),
'dataModel' of this JTable is based on the List 'model',
setResultOutput() expect String[3]
Thanks for everybodys help.
Regards.
Related
I'm trying to insert a CheckBox and a ComboBox on the same column, but it's a little bit tricky, I can't seem to find a solution for rendering both at once. Can someone help me with this? I would appreciate the help.
The Jtable I'm trying to achieve is below :
Probably this example will solve your problems
import java.awt.Component;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
/**
* <code>CellBasedTable</code>.
*/
public class CellBasedTable extends JTable {
private static class TableCell {
/** Column of the cell. */
private final int column;
/** Row of the cell. */
private final int row;
/**
* Creates cell with the given row and column.
*
* #param aRow row of cell.
* #param aColumn column of cell.
*/
public TableCell(int aRow, int aColumn) {
row = aRow;
column = aColumn;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof TableCell) {
TableCell another = (TableCell) obj;
return row == another.row && column == another.column;
}
return false;
}
#Override
public int hashCode() {
return Arrays.hashCode(new int[] {row, column});
}
}
private final Map<TableCell, TableCellRenderer> rendererMap = new HashMap<>();
private final Map<TableCell, TableCellEditor> editorMap = new HashMap<>();
/**
*
*/
public CellBasedTable() {
}
/**
* #param dm
*/
public CellBasedTable(TableModel dm) {
super(dm);
}
public void setCellRenderer(int row, int column, TableCellRenderer r) {
rendererMap.put(new TableCell(row, column), r);
}
#Override
public TableCellRenderer getCellRenderer(int row, int column) {
return rendererMap.getOrDefault(new TableCell(row, column), super.getCellRenderer(row, column));
}
public void setCellEditor(int row, int column, TableCellEditor e) {
editorMap.put(new TableCell(row, column), e);
}
#Override
public TableCellEditor getCellEditor(int row, int column) {
return editorMap.getOrDefault(new TableCell(row, column), super.getCellEditor(row, column));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(CellBasedTable::startDemo);
}
private static void startDemo() {
DefaultTableModel model = new DefaultTableModel(new Object[] {"Subject", "Value"}, 0);
model.addRow(new Object[] {"name", "Variable"});
model.addRow(new Object[] {"Stored", Boolean.TRUE});
model.addRow(new Object[] {"Stroed Arguments", "job"});
JComboBox<String> combo = new JComboBox<>(new String[] {"task", "job", "rule"});
CellBasedTable tbl = new CellBasedTable(model);
tbl.setCellRenderer(1, 1, tbl.getDefaultRenderer(Boolean.class));
tbl.setCellEditor(1, 1, tbl.getDefaultEditor(Boolean.class));
tbl.setCellRenderer(2, 1, new ComboRenderer(combo));
tbl.setCellEditor(2, 1, new DefaultCellEditor(combo));
tbl.setRowHeight(24);
JFrame frm = new JFrame("TableTest");
frm.add(new JScrollPane(tbl));
frm.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frm.pack();
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
private static class ComboRenderer implements TableCellRenderer {
private final JComboBox<?> combo;
/**
*
*/
public ComboRenderer(JComboBox<?> combo) {
this.combo = combo;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
combo.setSelectedItem(value);
combo.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
combo.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
return combo;
}
}
}
Good day!
I've been trying to solve this riddle with the tableModel listener, but can't seem to understand how it works and where to add addTableModelListener and fireTableChanged so that my code starts to listen to table data changes in the database. Would really appreciate some input. Thanks in advance! Here is the code:
ViewDataArea.java
import trip.TripAbstractTableModel;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ViewDataArea extends JComponent {
int yPosition = 60;
int xLabelPosition = 20;
int xFieldPosition = 220;
int labelWidth = 200;
int fieldWidth = 200;
int tableHeight = 400;
int tableWidth = 1155;
JButton emptyButton = new JButton("Refresh");
int buttonHeight = 28;
protected JPanel panel() {
JPanel panel = new JPanel();
panel.setLayout(null);
final JTable table = new JTable(new TripAbstractTableModel());
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(xLabelPosition, yPosition, tableWidth, tableHeight);
panel.add(scrollPane);
emptyButton.setBounds(xLabelPosition, yPosition - 30, labelWidth, buttonHeight);
panel.add(emptyButton);
emptyButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
return panel;
}
}
TripAbstractTableModel.java
package trip;
import org.hibernate.*;
import org.hibernate.cfg.*;
import java.util.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
public class TripAbstractTableModel extends AbstractTableModel implements TableModelListener {
private static final long serialVersionUID = 6105842825518764825L;
private ArrayList<trip.TripEntity> tripList;
public TripAbstractTableModel() {
super();
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Query q = session.createQuery("from trip.TripEntity");
tripList = new ArrayList<TripEntity>(q.list());
session.close();
sf.close();
}
public int getRowCount() {
return tripList.size();
}
public int getColumnCount() {
return 5;
}
public Object getValueAt(int rowIndex, int columnIndex) {
trip.TripEntity trip = tripList.get(rowIndex);
Object [] values = new Object[]{
trip.getId(), trip.getTripNumber(), trip.getExportCountry(), trip.getDestinationCountry(),
trip.getPriceFixed()};
return values[columnIndex];
}
#Override
public String getColumnName(int column) {
String[] columnNames = new String[]{"id","tripnumber","exportcountry","destinationcountry","pricefixed"};
return columnNames[column];
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void tableChanged(TableModelEvent e) {
System.out.println("Table Alert!");
}
}
updated: edited TripAbstractTableModel.java
package trip;
import org.hibernate.*;
import org.hibernate.cfg.*;
import java.util.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
public class TripAbstractTableModel extends AbstractTableModel implements TableModelListener {
private static final long serialVersionUID = 6105842825518764825L;
private ArrayList<trip.TripEntity> tripList;
public TripAbstractTableModel() {
super();
TripAbstractTableModel model = new TripAbstractTableModel();
model.addTableModelListener(this);
SessionFactory sf = new Configuration().configure().buildSessionFactory();
Session session = sf.openSession();
Query q = session.createQuery("from trip.TripEntity");
tripList = new ArrayList<TripEntity>(q.list());
session.close();
sf.close();
}
public int getRowCount() {
return tripList.size();
}
public int getColumnCount() {
return 5;
}
public Object getValueAt(int rowIndex, int columnIndex) {
trip.TripEntity trip = tripList.get(rowIndex);
Object [] values = new Object[]{
trip.getId(), trip.getTripNumber(), trip.getExportCountry(), trip.getDestinationCountry(),
trip.getPriceFixed()};
return values[columnIndex];
}
#Override
public String getColumnName(int column) {
String[] columnNames = new String[]{"id","tripnumber","exportcountry","destinationcountry","pricefixed"};
return columnNames[column];
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void tableChanged(TableModelEvent e) {
System.out.println("Table Alert!");
}
}
I would like to populate a JTable during runtime with many rows (lets say 10000). But all my attempts are very poor and inefficient.
Starting point is the addData method which gets a List of Objects representing a row. I tried to fill the table via a SwingWorker but this only works for small data for me.
Another attempt was setting the data directly without using any kind of thread, but this is also very slow, at least the UI isn't blocked like its the case with the SwingWorker.
So how do you do this is general? The table should be filled row by row or chunkwise but not all by one and the vertical scrollbar should be scrollable meanwhile.
My TableModel:
public class MyTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
String[] columnNames;
public Map<Long, ErrorMessage> data = new LinkedHashMap<Long, ErrorMessage>();
public MyTableModel(String[] header) {
columnNames = header;
}
public String getColumnName(int col) {
return columnNames[col].toString();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int row, int col) {
.
.
return value;
}
public void addRow(long id, MyDataObject o) {
data.put(id, m);
fireTableRowsInserted(0,nqm_messages.size()-1);
}
}
SwingWorker implementation:
class TableSwingWorker extends SwingWorker<MyTableModel, MyDataObject> {
private final MyTableModel tableModel;
List<MyDataObject> messages;
public TableSwingWorker(MyTableModel tableModel, List<MyDataObject> dataList) {
this.tableModel = tableModel;
this.messages = new LinkedList<MyDataObject>(mm);
}
#Override
protected MyTableModel doInBackground() throws Exception {
for(MyDataObject s : messages) {
publish(s);
}
return tableModel;
}
#Override
protected void process(List<MyDataObject> chunks) {
for(MyDataObject row : chunks){
Long l = Long.parseLong(row.getId());
tableModel.addRow(l, row);
}
}
}
Add Objects to JTable:
public void addData(List<MyDataObject> o) {
MyTableModel m = (MyTableModel)table.getModel();
(new TableSwingWorker(m,o)).execute();
//for(int i=0; i < mm.size();i++) {
// long l = Long.parseLong(mm.get(i).getId());
// m.addRow(l, mm.get(i));
//}
}
So, a number of things have being identified from the comments...
You need to correctly fire the row inserted method, indicating only those rows that have being added and where they have being updated. This very important, as the the table has being optimised for speed
You should provide batch add method for your table model, allowing you to more easily add multiple rows in a single or as few steps as possible
You should have the SwingWorker periodically sleep or yield, to allow it time to publish the results.
So, in this example, I'm adding 1, 000, 000 rows. In my test it took slightly under 1 second...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
public class TestTableLoad01 {
public static void main(String[] args) {
new TestTableLoad01();
}
public TestTableLoad01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MyTableModel model = new MyTableModel();
JTable table = new JTable(model);
table.setDefaultRenderer(Date.class, new TimeCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
TableSwingWorker worker = new TableSwingWorker(model);
worker.execute();
}
});
}
public class TimeCellRenderer extends DefaultTableCellRenderer {
private DateFormat df;
public TimeCellRenderer() {
df = new SimpleDateFormat("HH:mm:ss");
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Date) {
value = df.format(value);
}
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return this;
}
}
public class MyTableModel extends AbstractTableModel {
private String[] columnNames = new String[]{"Date", "Row"};
private List<RowData> data;
public MyTableModel() {
data = new ArrayList<>(25);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Date.class : Integer.class;
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int row, int col) {
RowData value = data.get(row);
return col == 0 ? value.getDate() : value.getRow();
}
public void addRow(RowData value) {
int rowCount = getRowCount();
data.add(value);
fireTableRowsInserted(rowCount, rowCount);
}
public void addRows(RowData... value) {
addRows(Arrays.asList(value));
}
private void addRows(List<RowData> rows) {
int rowCount = getRowCount();
data.addAll(rows);
fireTableRowsInserted(rowCount, getRowCount() - 1);
}
}
public class RowData {
private Date date;
private int row;
public RowData(int row) {
this.date = new Date();
this.row = row;
}
public Date getDate() {
return date;
}
public int getRow() {
return row;
}
}
public class TableSwingWorker extends SwingWorker<MyTableModel, RowData> {
private final MyTableModel tableModel;
public TableSwingWorker(MyTableModel tableModel) {
this.tableModel = tableModel;
}
#Override
protected MyTableModel doInBackground() throws Exception {
// This is a deliberate pause to allow the UI time to render
Thread.sleep(2000);
System.out.println("Start polulating");
for (int index = 0; index < 1000000; index++) {
RowData data = new RowData(index);
publish(data);
Thread.yield();
}
return tableModel;
}
#Override
protected void process(List<RowData> chunks) {
System.out.println("Adding " + chunks.size() + " rows");
tableModel.addRows(chunks);
}
}
}
I have the following "sample" code almost, but the column names dissapiared after ~2 hours codeing and i cant find where.
Can somebody tell me why the datamodel doesnt add the column names to the JTable?
My code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
class MyTableModel extends JPanel {
private static final int CHECK_COL = 3;
private static Object[][] DATA = {
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false},
{"","","",false},{"","","",false},{"","","",false},{"","","",false}
};
public static String[] COLUMNS = {"Debtid","Nev","VHO", "Check"};
protected DataModel dataModel = new DataModel(DATA, COLUMNS);
private JTable table = new JTable(dataModel);
private DefaultListSelectionModel selectionModel;
public MyTableModel(){
super(new BorderLayout());
this.add(table);
this.add(new ControlPanel(), BorderLayout.SOUTH);
table.setPreferredScrollableViewportSize(new Dimension(250, 175));
selectionModel = (DefaultListSelectionModel) table.getSelectionModel();
}
private class DataModel extends DefaultTableModel {
public DataModel(Object[][] data, Object[] COLUMNS) {
super(data, COLUMNS);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == CHECK_COL) {
return getValueAt(0, CHECK_COL).getClass();
}
return super.getColumnClass(columnIndex);
}
#Override
public boolean isCellEditable(int row, int column) {
return true;
}
}
private class ControlPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = -7342459414751761853L;
public ControlPanel() {
this.add(new JLabel("Selection:"));
this.add(new JButton(new SelectionAction("Clear", false)));
this.add(new JButton(new SelectionAction("Check", true)));
}
}
private class SelectionAction extends AbstractAction {
boolean value;
public SelectionAction(String name, boolean value) {
super(name);
this.value = value;
}
#Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < dataModel.getRowCount(); i++) {
if (selectionModel.isSelectedIndex(i)) {
dataModel.setValueAt(value, i, CHECK_COL);
}
}
}
}
public void setValueAt_Cell(String val, int row, int col) {
// TODO Auto-generated method stub
if(row==DATA.length){
DATA=cloneArray(DATA);
}
dataModel.setValueAt(val, row, col);
}
public static Object[][] cloneArray(Object[][] src) {
int length = src.length;
Object[][] target = new Object[length+1][src[0].length];
for (int i = 0; i < length; i++) {
System.arraycopy(src[i], 0, target[i], 0, src[i].length);
}
target[length] = new Object[]{"","","",false};
return target;
}
public String getValAtPos(int row,int col) {
//System.out.println(dataModel.getValueAt(row, col));
return dataModel.getValueAt(row, col).toString();
}
public int getRowCount(){
return DATA.length;
}
}
You need to wrap JTable with JScrollPane : this.add(new JScrollPane(table));.
I would like to populate a JTable during runtime with many rows (lets say 10000). But all my attempts are very poor and inefficient.
Starting point is the addData method which gets a List of Objects representing a row. I tried to fill the table via a SwingWorker but this only works for small data for me.
Another attempt was setting the data directly without using any kind of thread, but this is also very slow, at least the UI isn't blocked like its the case with the SwingWorker.
So how do you do this is general? The table should be filled row by row or chunkwise but not all by one and the vertical scrollbar should be scrollable meanwhile.
My TableModel:
public class MyTableModel extends AbstractTableModel {
/**
*
*/
private static final long serialVersionUID = 1L;
String[] columnNames;
public Map<Long, ErrorMessage> data = new LinkedHashMap<Long, ErrorMessage>();
public MyTableModel(String[] header) {
columnNames = header;
}
public String getColumnName(int col) {
return columnNames[col].toString();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int row, int col) {
.
.
return value;
}
public void addRow(long id, MyDataObject o) {
data.put(id, m);
fireTableRowsInserted(0,nqm_messages.size()-1);
}
}
SwingWorker implementation:
class TableSwingWorker extends SwingWorker<MyTableModel, MyDataObject> {
private final MyTableModel tableModel;
List<MyDataObject> messages;
public TableSwingWorker(MyTableModel tableModel, List<MyDataObject> dataList) {
this.tableModel = tableModel;
this.messages = new LinkedList<MyDataObject>(mm);
}
#Override
protected MyTableModel doInBackground() throws Exception {
for(MyDataObject s : messages) {
publish(s);
}
return tableModel;
}
#Override
protected void process(List<MyDataObject> chunks) {
for(MyDataObject row : chunks){
Long l = Long.parseLong(row.getId());
tableModel.addRow(l, row);
}
}
}
Add Objects to JTable:
public void addData(List<MyDataObject> o) {
MyTableModel m = (MyTableModel)table.getModel();
(new TableSwingWorker(m,o)).execute();
//for(int i=0; i < mm.size();i++) {
// long l = Long.parseLong(mm.get(i).getId());
// m.addRow(l, mm.get(i));
//}
}
So, a number of things have being identified from the comments...
You need to correctly fire the row inserted method, indicating only those rows that have being added and where they have being updated. This very important, as the the table has being optimised for speed
You should provide batch add method for your table model, allowing you to more easily add multiple rows in a single or as few steps as possible
You should have the SwingWorker periodically sleep or yield, to allow it time to publish the results.
So, in this example, I'm adding 1, 000, 000 rows. In my test it took slightly under 1 second...
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
public class TestTableLoad01 {
public static void main(String[] args) {
new TestTableLoad01();
}
public TestTableLoad01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
MyTableModel model = new MyTableModel();
JTable table = new JTable(model);
table.setDefaultRenderer(Date.class, new TimeCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
TableSwingWorker worker = new TableSwingWorker(model);
worker.execute();
}
});
}
public class TimeCellRenderer extends DefaultTableCellRenderer {
private DateFormat df;
public TimeCellRenderer() {
df = new SimpleDateFormat("HH:mm:ss");
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof Date) {
value = df.format(value);
}
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return this;
}
}
public class MyTableModel extends AbstractTableModel {
private String[] columnNames = new String[]{"Date", "Row"};
private List<RowData> data;
public MyTableModel() {
data = new ArrayList<>(25);
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Date.class : Integer.class;
}
#Override
public String getColumnName(int col) {
return columnNames[col];
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public int getRowCount() {
return data.size();
}
#Override
public Object getValueAt(int row, int col) {
RowData value = data.get(row);
return col == 0 ? value.getDate() : value.getRow();
}
public void addRow(RowData value) {
int rowCount = getRowCount();
data.add(value);
fireTableRowsInserted(rowCount, rowCount);
}
public void addRows(RowData... value) {
addRows(Arrays.asList(value));
}
private void addRows(List<RowData> rows) {
int rowCount = getRowCount();
data.addAll(rows);
fireTableRowsInserted(rowCount, getRowCount() - 1);
}
}
public class RowData {
private Date date;
private int row;
public RowData(int row) {
this.date = new Date();
this.row = row;
}
public Date getDate() {
return date;
}
public int getRow() {
return row;
}
}
public class TableSwingWorker extends SwingWorker<MyTableModel, RowData> {
private final MyTableModel tableModel;
public TableSwingWorker(MyTableModel tableModel) {
this.tableModel = tableModel;
}
#Override
protected MyTableModel doInBackground() throws Exception {
// This is a deliberate pause to allow the UI time to render
Thread.sleep(2000);
System.out.println("Start polulating");
for (int index = 0; index < 1000000; index++) {
RowData data = new RowData(index);
publish(data);
Thread.yield();
}
return tableModel;
}
#Override
protected void process(List<RowData> chunks) {
System.out.println("Adding " + chunks.size() + " rows");
tableModel.addRows(chunks);
}
}
}