I have used a tutorial to see how to implement JTable but I don't know how exactly access the data of each cell to extract the data that the user put in these.
The table has 2 column and N rows
In the first column there is a String in the second there is a int
The tutorial that I have used is this
Every JTable has a data-model connected with it. Users may add data to this data model (e.g. by calling to the javax.swing.table.TableModel.setValueAt(Object, int, int) method) and JTable then displays them. In order to process the data from the JTable one can use the following approach:
JTable t = new JTable(/* set some table-model that will contain the data */);
...
/* get some table-model that will contain the data */
TableModel tm = t.getModel();
for (int i = 0; i < tm.getRowCount(); i++) {
for (int j = 0; j < tm.getColumnCount(); j++) {
Object o = tm.getValueAt(i, j);
if (o instanceof Integer) {
System.out.println((Integer)o);
} else if (o instanceof String) {
System.out.println((String)o);
}
}
}
You have to see the paragraph "Listening for Data Changes"
In general, you have to get the model associated with the table and call it "getValueAt" method. It returns the Object associated with the cell, so you have to cast it to String or Integer.
For example if you want to get the value of the second column and third row, the code is:
(Integer) model.getValueAt(2,1)
Related
I've to create a java swing app that allows the user to menage some Oracle tables.
I've implemented the JTable in this way:
public class TableModel extends AbstractTableModel{
.
.
.
public List <List <String> > result = null;
.
.
.
public void createTable(ResultSet r) throws SQLException {
rs = r;
numcols = getColumnCount();
result = new ArrayList();
while (rs.next()) {
List <String> row = new ArrayList<>(numcols);
for (int i=1; i<= numcols; i++)
row.add(rs.getString(i).replace("00:00:00.0", "")); //just managing stuffs .. nothing important. Don't care of this replace.
result.add(row);
}
fireTableStructureChanged();
}
public void addRow(){
List <String> row = new ArrayList<>();
for(int i=1;i<=getColumnCount();i++)
row.add("");
result.add(row);
fireTableStructureChanged();
}
public Object getValueAt(int row, int col) {
return result.get(row).get(col);
}
public int getColumnCount() {
if (rs == null) {
return 0;
}
try {
return rs.getMetaData().getColumnCount();
} catch (SQLException e) {
System.err.println("error in colcount");
return 0;
}
}
public int getRowCount() {
int totalRows = 0;
try {
rs.last();
totalRows = rs.getRow();
rs.beforeFirst();
} catch(Exception ex) {
return 0;
}
return totalRows ;
}
}
so, I store database data into the arraylist and then I print the table using the arraylist.
Now in the addRow() function I add to that arraylist an empty record (in my mind an empty record is a record composed of all null ("") strings).
Finally using the fireTableStructureChanged() method I expect the table to refresh itself.. but this will never occur and I don't understand why.
I've already checked that the new row is successfully loaded into the arraylist:
I got all the data stored in the table of the Database into the arraylist and the new row is successfully loaded in the arraylist.
so I swear the problem is about that fireTableStructureChanged() method.
Thanks :3
Try using fireTableRowsInserted(int firstRow, int lastRow) instead of fireTableStructureChanged()
Here's the javadoc for the structure changed ...
Notifies all listeners that the table's structure has changed. The number of columns in the table, and the names and types of the new columns may be different from the previous state. If the JTable receives this event and its autoCreateColumnsFromModel flag is set it discards any table columns that it had and reallocates default columns in the order they appear in the model. This is the same as calling setModel(TableModel) on the JTable.
So that one will only work if the columns types changed ...
If you are lazy go for this one: fireTableDataChanged()
I found the solution.
That's quite embarassing..
#tsolakp you helped me a lot!
Since before using ArrayList I was working on ResultSet directly, I forgot to update getrowcount.
So, since the inserting was only "client side" on the arraylist, the rows displayed were only "n" (where n was the number of rows on the database). So I would never displayed my new row since it was not loaded into the database.
That's all :)
Maybe working hard on coding, makes these errors invisible :X
I have a table with a million rows.
If I select all rows and Copy, it takes approximately 5 minutes before responsiveness returns to the GUI and before I can paste it into another application. This is less than great, so I started looking for improvements.
If I make an alternative thread-safe TableModel, then I can background the operation of building the string and then push to the clipboard (on the EDT, if need be). Then the GUI is responsive, but it is still 5 minutes before I can paste into another application, which in some ways is a worse experience, because at least when it blocked the EDT, I was able to tell when the operation finished. I know I could add GUI feedback for it, but it seems wrong for an action like Copy.
If I look at the same thing happening between native applications, I notice that I can copy an enormous amount of data from one application with no delay. I can then immediately paste it into another application with no delay, but just with the caveat that it can take a long time for all the information to end up in there. For instance, if I paste huge text from TextEdit into Terminal, it seems to do this a bit at a time somehow.
Is there some way to do that in AWT as well?
I attempted to do it by only declaring the alternate flavours returning Reader and InputStream, but it turns out that even if you do this, it asks for the reader one, fetches the reader and then immediately reads the entire stream, storing it as a string. So this just moves the 5 minute delay to code inside the JRE, solving nothing. This is also fairly stupid, because there is this whole framework around what type of data transfer methods your Transferable supports, yet no matter which type you expose, the system seems to convert it down to the same type, which in the best case will take forever to build, or in the worst case will run you out of heap.
Maybe there is a type which doesn't though, so I thought I'd post this question to ask whether anyone knows.
Example of one TransferHandler, although the built-in one on JTable itself exhibits the same issue.
private class CombinedTransferHandler extends TransferHandler {
#Override
protected Transferable createTransferable(JComponent component) {
if (component instanceof JTable) {
JTable table = (JTable) component;
int[] rows = table.getSelectedRows();
if (!table.getRowSelectionAllowed()) {
int rowCount = table.getRowCount();
rows = new int[rowCount];
for (int counter = 0; counter < rowCount; counter++) {
rows[counter] = counter;
}
}
// columns omitted because we know we use all
return new BasicTransferable(getPlainData(rows), null);
}
return null;
}
#Override
public int getSourceActions(JComponent c) {
return COPY;
}
private String getPlainData(int[] rows) {
StringBuilder result = new StringBuilder();
int staticColumnCount = staticTable.getColumnCount();
int mainColumnCount = mainTable.getColumnCount();
for (int row : rows) {
for (int viewColumn = 0; viewColumn < staticColumnCount;
viewColumn++) {
if (viewColumn > 0) {
result.append('\t');
}
Object value = staticTable.getValueAt(row, viewColumn);
value = (value == null) ? "" : value.toString();
result.append(value);
}
for (int viewColumn = 0; viewColumn < mainColumnCount;
viewColumn++) {
if (staticColumnCount > 0 || viewColumn > 0) {
result.append('\t');
}
Object value = mainTable.getValueAt(row, viewColumn);
value = (value == null) ? "" : value.toString();
result.append(value);
}
result.append('\n');
}
return result.toString();
}
}
Please read full before marking as duplicate. I have seen all the related/similar questions already and I guess I have something weird happening here.
I'm using Apache POI version 3.9 to generate a 4 sheet workbook. Every time I'm done filling the sheet, I auto-size the columns. Here's the code:
for (String alias : map.keySet()) {
Sheet sheet = workbook.createSheet(getSheetNameForAlias(alias));
List<Object[]> resultList = (List<Object[]>) map.get(alias);
Collections.sort(resultList.subList(1, resultList.size()), getComparator(alias));
int rownum = 0;
for (Object[] array : resultList) {
rownum = populateRow(cellStyles, sheet, rownum, array, false);
}
for(int j=0; j< resultList.get(0).length; j++){
sheet.autoSizeColumn(j, false);
}
}
the populateRow function is:
private int populateRow(CellStyle[] cellStyles, Sheet sheet, int rownum, Object[] dataArr, boolean doAutoSizing) {
if (logger.isDebugEnabled()) {
logger.debug("Populating row ... #{} : {} ", sheet.getSheetName(), rownum);
}
int cellnum;
Row row = sheet.createRow(rownum++);
cellnum = 0;
if (ArrayUtils.isEmpty(dataArr)) {
logger.error("No data found for row ... #{} : {} ", sheet.getSheetName(), rownum);
return rownum;
}
for (Object obj : dataArr) {
Cell cell = row.createCell(cellnum++);
// get the type of the data inserted in the cell.
// Accordingly set the cell value
String data = String.valueOf(obj);
if (StringUtils.isBlank(data) || "null".equals(data)) {
cell.setCellValue(StringUtils.EMPTY);
} else {
if (NumberUtils.isNumber(data)) {
Double dbVal = NumberUtils.createDouble(data);
// Millions separator for Numeric fields
if (dbVal == Math.ceil(dbVal)) {
cell.setCellStyle(cellStyles[0]);
} else {
cell.setCellStyle(cellStyles[1]);
}
cell.setCellType(Cell.CELL_TYPE_NUMERIC); // for numeric , set cell type as numeric
cell.setCellValue(dbVal);
} else {
if(obj instanceof java.sql.Timestamp || obj instanceof java.sql.Date){
cell.setCellStyle(cellStyles[2]);
}
cell.setCellValue(data);
}
if (doAutoSizing) {
sheet.autoSizeColumn(cellnum - 1);
}
data = null;
}
obj = null;
}
return rownum;
}
The weird part is, columns of one out of the four sheets are getting resized incorrectly.
I tried re-writing the auto size logic with different approaches in my mind and discovered that the sheet, for which I'm getting this incorrect behaviour, if I try sheet.getRow(0), it gives out null which specifies that there is no data for that row but the excel being generated does not hold good with that, all four sheets have appropriate data. And in every case, the first row is always the headings so It couldn't be null even if there is no data beneath it.
I don't know what I'm missing but I tried and found that it is not a Font issue with the JVM.
Please let me know in case you need some other info.
EDIT: Observed that auto-sizing is working incorrectly for the sheet which has more than 100 rows. I don't know how to relate it to some logic. This is real WEIRD.
EDIT 2: I found the official reference for the reason given by Axel while looking for something else. This does suffice my confusion. Read the para on the following
link
Since you say it works up to 100 rows, it looks like you are using SXSSFWorkbook, not XSSFWorkbook. Then that behaviour has to be expected, since only a fixed number of rows are kept in memory, and only these rows will be considered. So, if I am right, you will have to switch to using XSSFWorkbook (provided this doesn't lead to OOMs when dealing with a large number of rows).
I am new to JavaFX and would like to know how to set and get the cell value of JavaFX Table like Swing JTable. i.e. an alternative of setValueAt() & getValueAt of Swing JTable in JavaFX Table.
//would like to get the index of column by Name
int index_RunnableQueueItem = tableQueue.getColumns().indexOf("RunnableQueueItem");
//would like to get the selected row
int row = tableQueue.getSelectionModel().getSelectedIndex();
if (index_RunnableQueueItem != -1 && row != -1) {
// would like to get the value at index of row and column.
//Update that value and set back to cell.
}
TableView really doesn't support this methodology.
Here's a somewhat brittle means of doing what you want, using reflection. It's entirely dependent upon you using PropertyValueFactory in your cell value factory so it can lookup the property name, though.
class MyItem
{
SimpleStringProperty nameProperty = new SimpleStringProperty("name");
public MyItem(String name) {
nameProperty.set(name);
}
public String getName() { return nameProperty.get(); }
public void setName(String name) { nameProperty.set(name); }
public SimpleStringProperty getNameProperty() { return nameProperty; }
}
...
TableView<MyItem> t = new TableView<MyItem>();
TableColumn col = new TableColumn("Name Header");
col.setCellValueFactory(new PropertyValueFactory<MyItem, String>("name"));
t.getColumns().addAll(t);
...
public void setValue(int row, int col, Object val)
{
final MyItem selectedRow = t.getItems().get(row);
final TableColumn<MyItem,?> selectedColumn = t.getColumns().get(col);
// Lookup the propery name for this column
final String propertyName = ((PropertyValueFactory)selectedColumn.getCellValueFactory()).getProperty();
try
{
// Use reflection to get the property
final Field f = MyItem.class.getField(propertyName);
final Object o = f.get(selectedRow);
// Modify the value based on the type of property
if (o instanceof SimpleStringProperty)
{
((SimpleStringProperty)o).setValue(val.toString());
}
else if (o instanceof SimpleIntegerProperty)
{
...
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
Retrieving a simple value from a JavaFx TableView Cell
You can use listeners as listed in other posts, but if you wish to get just a simple value from a cell you can use a simpler method
Example:
// Get the row index where your value is stored
int rowIndex = tableView.getSelectionModel().getSelectedIndex();
// Since you can't get the value directly from the table like the
// getValueAt method in JTable, you need to retrieve the entire row in
// FXCollections ObservableList object
ObservableList rowList =
(ObservableList) tableViewModelos.getItems().get(rowIndex);
// Now you have an ObservableList object where you can retrieve any value
// you have stored using the columnIndex you now your value is, starting
// indexes at 0;
// In my case, I want to retrieve the first value corresponding to the first column //index, and I know it is an Integer Value so I'll cast the object.
int columnIndex = 0;
int value = Integer.parseInt(rowList.get(columnIndex).toString());
Hope this expample helps you.
I have a JTable and when I use jTable1.getModel()).getDataVector() objects from different columns have different types (they should be all Strings for my case)
My table has five columns:
number (it's String actually, but I have no problem with parsing it)
String chosen from combobox attached to cell, but when I want to take value from it it appears to be Vector, not a String
again number (like in 0 column - no problem here)
again String with combobox, but gives me String value, so no problem
number (hidden in String) but again this should be String but is Vector
how I create comboboxes in cells (column 1 and 3):
TableColumn column = jTable1.getColumnModel().getColumn(3);
JComboBox cb = new JComboBox(ServerIntf.DataModificationType.getStringList().toArray());
column.setCellEditor(new DefaultCellEditor(cb));
how I create table model:
public TableModel getTableModel() {
Vector<Vector<String>> data = task.getDataSet(dataName);
ServerIntf.SimpleDataType sdt = Manager.manager.getSimpleDataType(task, dataName);
rowsMin = sdt.getRowMin();
rowsMax = sdt.getRowMax();
columnsMin = sdt.getColMin();
columnsMax = sdt.getColMax();
if (data != null) {
//nothing important here, it doeasn't come into this part anyway
}
int cmax = 5;
int rmax = 1;
Vector columns = new Vector(cmax);
columns.addAll(Arrays.asList(new String[]{"ID", "PrzeksztaĆcenie", "Ile razy", "Co z danymi", "Co dalej"}));
return new DefaultTableModel(columns, rmax);
}
And this is how I read data:
public AlgorythmTable(List get) {
steps = new Vector<Step>();
for (List<String> row : (List<List<String>>) get) {
steps.add(new Step(row));
}
//boring here
}
//...
public Step(List list) {
id = Integer.parseInt((String) list.get(0));
matrix = ((String) ((Vector) list.get(1)).get(0)).isEmpty() ? null : ((String) ((Vector) list.get(1)).get(0));
count = ((String) list.get(2))==null || ((String) list.get(2)).isEmpty() ? 0 : Integer.parseInt((String) list.get(2));
after = ((String) list.get(3)).isEmpty() ? null : ServerIntf.DataModificationType.getByName((String) list.get(3));
nextStep = ((String) list.get(4))==null || ((String) list.get(4)).isEmpty() ? -1 : Integer.parseInt(((String) list.get(1)));
}
I get CastException at the last line (nextStep = ...)
Exception occurred during event dispatching:
java.lang.ClassCastException: java.util.Vector cannot be cast to java.lang.String
I was getting the same exception at the line (matrix = ...) but as you can see I casted it differently and now it seems to work fine.
I thought at first that the problem was connected to the comboboxes in cells, but it also occurs in column where there is simple String inserted by the user.
Well I can simply do another casting but it bothers me, because it should work without it (didn't have that problem in different kinds of JTables), this makes code hard to read and if will ever want to expand this table with more columns (which might happen in near future) and I will struggle again with the same problem.
Does anyone have any idea, why does it work like that and if there's any way of forcing jtable (or model) to give me values of cells in unified format?
Instead of coercing your List<List<String>> into a DefaultTableModel, it may be more maintainable to extend AbstractTableModel, as suggested in Creating a Table Model. In particular, you override getColumnClass() to specify the precise type of each datum. This has the direct benefit of enabling several default Editors and Renderers. There's a related example here.