Java CustomTableModel can't sort properly - java

I add items to a model that is linked to a table. When I select an item in this table, things happen depending on what item it is. For now I just have a System.out telling me the items name.
If I have two items called 'A' and 'B', when I select either their respective name is written to the console as expected, however, if I sort them by name, so that 'B' is placed in the row over 'A', the sorting never happened internally but only visually. So if I now select 'A', the console prints out 'B', and vice versa.
The sorter is declared in the mainclass, itemList is a JTable itemList.setAutoCreateRowSorter(true);
Apparently I must have failed to include some default method that's needed for this sorterfunctionality. "default methods" is declared towards the end in the code-snippet, from and after the method 'getColumnName'.
class ItemModel extends AbstractTableModel
{
ArrayList<MCItem> items = new ArrayList<MCItem>();
private int currentMaxRows = 0;
private String[] columnNames = {"Item", "Total Units", "In Sorter"};
private Class[] types = {String.class, Integer.class, Integer.class};
private Object[][] data = new Object[currentMaxRows][getColumnCount()];
public ArrayList<MCItem> getItems()
{
return items;
}
public void readdItems(Main m, ArrayList<MCItem> tempItems)
{
for(MCItem mci : tempItems)
{
mci.setMain(m);
addRow(mci);
}
}
public void emptyMe()
{
currentMaxRows = 0;
items.clear();
data = new Object[currentMaxRows][getColumnCount()];
fireTableDataChanged();
}
public boolean isDuplicate(String s)
{
for(MCItem ci : items)
if(ci.getName().equalsIgnoreCase(s))
return true;
return false;
}
public void updateItem(String id)
{
try
{
int foundRow = -1;
for(int i = 0;i < currentMaxRows;i++)
if(getValueAt(i, 0).toString().equalsIgnoreCase(id))
{
foundRow = i;
break;
}
for(MCItem ii : items)
if(ii.getName().equalsIgnoreCase(id))
{
setItem(foundRow, ii);
fireTableDataChanged();
return;
}
}
catch(NullPointerException e){}
}
public void addRow(MCItem item)
{
//check if we need to expand the dataArray
if(currentMaxRows == items.size())
{
if(currentMaxRows == 0)
data = new Object[++currentMaxRows][getColumnCount()];
else
{
Object[][] tempArr = data;
data = new Object[++currentMaxRows][getColumnCount()];
for(int x = 0; x < tempArr.length; x++)
for(int y = 0; y < getColumnCount(); y++)
data[x][y] = tempArr[x][y];
}
}
setItem(items.size(), item);
items.add(item);
fireTableDataChanged();
}
public void changeItem(int row, String name)
{
String originalName = (String) data[row][0];
data[row][0] = name;
for(MCItem ii : items)
if(ii.getName().toString().equalsIgnoreCase(originalName))
{
ii.setName(name);
return;
}
fireTableDataChanged();
}
public void removeItem(String id)
{
for(MCItem ii : items)
if(ii.getName().toString().equalsIgnoreCase(id))
{
items.remove(ii);
redoList();
return;
}
fireTableDataChanged();
}
private void redoList()
{
ArrayList<MCItem> tempArr = (ArrayList<MCItem>) items.clone();
emptyMe();
for(MCItem ii : tempArr)
addRow(ii);
}
private void setItem(int row, MCItem item)
{
int counter = 0;
data[row][counter++] = item.getName();
data[row][counter++] = item.getCount();
data[row][counter++] = item.getSorterCount();
fireTableDataChanged();
}
MCItem getMCItem(String name)
{
for(MCItem i : items)
if(i.getName().equals(name))
return i;
return null;
}
public String getColumnName(int col)
{
return columnNames[col].toString();
}
public int getRowCount()
{
return data.length;
}
public int getColumnCount()
{
return columnNames.length;
}
public Object getValueAt(int row, int col)
{
return data[row][col];
}
public Class getColumnClass(int columnIndex)
{
return this.types[columnIndex];
}
public boolean isCellEditable(int row, int col)
{
return false;
}
public void setValueAt(Object value, int row, int col)
{
data[row][col] = value;
fireTableCellUpdated(row, col);
}
}
* Answer *
The issue was never the tablemodel, but the JTable itself. When I want to present information based on the item selected, I called
currentMCItem = model.getMCItem(model.getValueAt(itemList.getSelectedRow(), 0).toString());
which returned the index in the JTable correctly, however when sorting all the indexes gets messed up and it's only the view that changes, so I had to redo that line to
currentMCItem = model.getMCItem(model.getValueAt(itemList.convertRowIndexToModel(itemList.getSelectedRow()), 0).toString());
So, the key is to call JTable.convertRowIndexToModel(SELECTED INDEX IN TABLE) in order to get the correct index, and use that as if it was the selectedRow.

You have a set of convert methods in JTable that you need to use. For example, convertColumnIndexToModel takes a view index and gives you back a corresponding column index in the model. Convert them and then get the values.

Related

My Jtable Model doesnt make my boolean a checkbox

sorry but I still don't get this thing from my database dont change to check box. it just show as true or false
try {
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT * FROM tblpdareenlist");
DefaultTableModel model = (DefaultTableModel) viewRecordTable.getModel();
int x = 0;
String b, c, d, e, f, g, h, i;
Boolean a;
while (rs.next()) {
a = rs.getBoolean("colAttend");
b = rs.getString("colNr");
c = rs.getString("colRank");
d = rs.getString("colFName");
e = rs.getString("colMName");
f = rs.getString("colLName");
g = rs.getString("colSN");
h = rs.getString("colUnit");
i = rs.getString("colETE");
model.insertRow(x, new Object[]{a,b,c,d,e,f,g,h,i});
x++;
}
} catch (Exception e) {
JOptionPane.showMessageDialog(rootPane, "Syntax Error\n" + e);
}
The JTable will display a check box for a column if the table model's getColumnClass method returns Boolean.class for that column[1]. You are using the DefaultTableModel which only returns Object.class for any column. This model is just a very simple model, not the best for a real system, more for testing or very simple data.
The best is to implement your own TableModel returning the correct class for given column, e.g. extending the AbstractTableModel.
For testing only you can just extend the DefaultTableModel and override the getColumnClass method:
var model = new DefaultTableModel(0, 3) {
#Override
public Class<?> getColumnClass(int col) {
if (col == 0) return Boolean.class;
else return super.getColumnClass(col);
}
};
var table = new JTable(model);
model.insertRow(0, List.of(false, "false", 0).toArray());
model.insertRow(1, List.of(true, "true", 1).toArray());
JOptionPane.showMessageDialog(null, table);
Again, on the long run, for a real project, you will get stuck very soon with that solution - much better to implement a dedicated TableModel. A very simple (incomplete) example:
public static void main(String[] args) {
var model = new MyTableModel();
var table = new JTable(model);
model.add(new MyData(false, "false", 0));
model.add(new MyData(true, "true", 1));
JOptionPane.showMessageDialog(null, table);
}
static final int FLAG = 0;
static final int NAME = 1;
static final int VALUE = 2;
private static class MyTableModel extends AbstractTableModel {
private final List<MyData> list = new ArrayList<>();
void add(MyData data) {
list.add(data);
}
#Override
public int getRowCount() {
return list.size();
}
#Override
public int getColumnCount() {
return 3;
}
#Override
public Object getValueAt(int row, int col) {
var data = list.get(row);
switch(col) {
case FLAG: return data.flag;
case NAME: return data.name;
case VALUE: return data.value;
}
throw new IllegalArgumentException("column " + col);
}
#Override
public Class<?> getColumnClass(int col) {
switch(col) {
case FLAG: return Boolean.class;
case NAME: return String.class;
case VALUE: return Integer.class;
}
throw new IllegalArgumentException("column " + col);
}
}
private static class MyData {
final boolean flag;
final String name;
final int value;
MyData(boolean flag, String name, int value) {
this.flag = flag;
this.name = name;
this.value = value;
}
}
[1] JTable Tutorial

Jtable will not save data of the cell when editing

I have JTable that has a column editable. It should get Integer values and update table. But when I edit a cell and go to another cell, the data will erase and goes back to null but the program does not throw any exceptions. How can I fix it?
public class FoodListPanel extends JPanel{
JTable jTable;
FoodTableModel fm;
public FoodListPanel() {
try {
fm = new FoodTableModel(FoodDAO.getAllFoodsFromDB(), new ArrayList<Integer>());
jTable = new JTable(fm) {
public boolean isCellEditable(int data, int columns) {
if(columns<5){
return false;
}
else if(columns ==5){
return true;
}
else if(columns ==6){
if(getValueAt(data, 5)==Boolean.FALSE){
return false;
}
else {
return true;
}
}
else{
return true;
}
}
public Component prepareRenderer(TableCellRenderer r, int data, int columns) {
Component c = super.prepareRenderer(r, data, columns);
return c;
}
};
jTable.setPreferredScrollableViewportSize(new Dimension(650, 420));
jTable.setFillsViewportHeight(true);
JScrollPane jScrollPane = new JScrollPane(jTable);
add(jScrollPane);
} catch (SQLException e) {
e.printStackTrace();
}
}
class FoodTableModel extends AbstractTableModel {
protected String[] cols = {"نام‌غذا", "دسته‌بندی", "قیمت", "توضیحات", "عکس" , "تعداد"};
protected Class[] colClasses = {String.class, Integer.class, String.class, String.class,
JLabel.class, Integer.class};
ArrayList<Food> al;
ArrayList<Integer> vals;
public FoodTableModel(ArrayList<Food> foods, ArrayList<Integer> val) {
al = new ArrayList<Food>();
al.addAll(foods);
vals = new ArrayList<Integer>(al.size());
vals.addAll(val);
}
////// TODO: 8/20/16 make dynamic from DB
public int getColumnCount (){return cols.length;}
public int getRowCount (){return al.size();}
public String getColumnName(int col) { return cols[col]; }
public Class getColumnClass(int col) { return colClasses[col]; }
public Object getValueAt(int row, int col){
switch (col) {
case 0:
return al.get(row).getName();
case 1:
return al.get(row).getType();
case 2:
return al.get(row).getPrice();
case 3:
return al.get(row).getDiscreption();
case 5:
if(vals.size()>= al.size()) return vals.get(row);
default:
return null;
}
}
////https://stackoverflow.com/questions/39066012/index-out-of-bound-exception-when-setting-value-to-jtable
public void setValueAt(Object vlaue, int row, int column){
if (column==6){
this.vals.set(row, (Integer)vlaue);
}
this.fireTableCellUpdated(row, column);
this.fireTableDataChanged();
}
/*public void setCols(String[] columns){
cols = columns;
}
public void setClasses(Class[] classes){
colClasses = classes;
}*/
}
}
the data will erase and goes back to null
Your getValuaAt(..) and setValueAt() methods are out of sync.
In the setValueAt() you only save the data for column 6.
In the getValueAt() you only return the data for columns 0-5 and return null for column 6.
You need to fix the getValueAt(...) method to return the actual data, not null.
Also, the setValueAt(...) method should only invoke the fireTableCellUpdated(...) method not the fireTableDataChanged(...) method.

show specific column in JTable with a predefined model

I have a JTable with 10 columns headers: "A","d","e","f","B","g","h","C","i","j".I want in first view JTable Show only "A","B","C" and I have two JButton when clicked on ViewAll Button all columns show and when clicked on hide Jtable show only three columns with "A","B","C" headers.how can do it?my GridTableModel is:
public abstract class GridTableModel<T> extends AbstractTableModel {
private static final long serialVersionUID = 4283080272635443348L;
private List<T> rows = new ArrayList<T>();
/**
* The property used to find real index of rows that currently are shown to user.
*/
private int offset;
public abstract String[] getColumnNames();
#Override
public String getColumnName(int column) {
return getColumnNames()[column];
}
#Override
public int getColumnCount() {
return getColumnNames().length;
}
#Override
public int getRowCount() {
return rows == null ? 0 : rows.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rows.size() > 0) {
return getValueAt(rows.get(rowIndex), rowIndex, columnIndex);
}
return null;
}
public void setData(List<T> results, int offset) {
this.rows = results;
this.offset = offset;
fireTableDataChanged();
}
public T get(int row) {
return rows.size() > 0 ? rows.get(row) : null;
}
public abstract Object getValueAt(T t, int rowIndex, int columnIndex);
public List<T> get(int[] rows) {
List<T> list = new ArrayList<T>();
for (int row : rows) {
list.add(this.rows.get(row));
}
return list;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public List<T> getRows() {
return rows;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (1==1) {
return Object.class;
}
return getValueAt(0, columnIndex).getClass();
}
}

How can I print multiple tables side by side in one job?

How can I print three different tables side by side or add them together in one printing? since the following code prints separately and in different pages.
I have little knowledge with the printing aspect of JTable's.
I am using NetBeans 8.0.
Or could I instead import the data to an excel file and print it from there? Is that plausible?
edit: All three tables must be together, side by side when it print, regardless if its portrait or landscape. Even better if all the tables or joined and is represented by multiple columns instead of three separate tables.
PrinterJob job = PrinterJob.getPrinterJob();
PrintRequestAttributeSet asset = new HashPrintRequestAttributeSet();
PageFormat pf = job.pageDialog(asset);
job.setPrintable(new recording_system(), pf);
boolean ok = job.printDialog(asset);
if (ok) {
try {
jTstudents.print(JTable.PrintMode.NORMAL);
jTscores.print(JTable.PrintMode.NORMAL);
jTresults.print(JTable.PrintMode.NORMAL);
} catch (PrinterException ex) {
/* The job did not successfully complete */
}
}
The tables look like this
Implement a TableModel class that combines all models.
TableModel model = new ParallelTableModel(jTStudents.getModel(),
jTScores.getModel(),
TResults.getModel());
JTable totalTable = new JTable(model);
Copy table header.
And then totalTable.getPrintable for printing.
/**
* Table Model composed from side-by-side table models.
*/
public class ParalleTableModel extends AbstractTableModel {
private final TableModel[] models;
public ParalleTableModel(TableModel... models) {
this.models = models;
}
#Override
public int getRowCount() {
return models[0].getRowCount();
}
#Override
public int getColumnCount() {
int count = 0;
for (TableModel model : models) {
count += model.getColumnCount();
}
return count;
}
#Override
public String getColumnName(int columnIndex) {
int count = 0;
for (TableModel model : models) {
int n = model.getColumnCount();
if (columnIndex < count) {
return model.getColumnName(columnIndex - count);
}
count += n;
}
throw new IndexOutOfBoundsException();
}
#Override
public Class<?> getColumnClass(int columnIndex) {
int count = 0;
for (TableModel model : models) {
int n = model.getColumnCount();
if (columnIndex < count) {
return model.getColumnClass(columnIndex - count);
}
count += n;
}
throw new IndexOutOfBoundsException();
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
int count = 0;
for (TableModel model : models) {
int n = model.getColumnCount();
if (columnIndex < count) {
return model.isCellEditable(rowIndex, columnIndex - count);
}
count += n;
}
throw new IndexOutOfBoundsException();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
int count = 0;
for (TableModel model : models) {
int n = model.getColumnCount();
if (columnIndex < count) {
return model.getValueAt(rowIndex, columnIndex - count);
}
count += n;
}
throw new IndexOutOfBoundsException();
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
int count = 0;
for (TableModel model : models) {
int n = model.getColumnCount();
if (columnIndex < count) {
model.setValueAt(aValue, rowIndex, columnIndex - count);
}
count += n;
}
throw new IndexOutOfBoundsException();
}
}

NullPointer in AbstractTableModel

I am trying to substitute the null value of VCID and VCIDBACKUP for "Dont Have". Here is my code:
if (controladorExcel == false) {
WritableWorkbook workbookVazio = Workbook.createWorkbook(file);
WritableSheet sheet1 = workbookVazio.createSheet("First Sheet", 0);
TableModel model = table.getModel();
for (int i = 0; i < model.getColumnCount(); i++) {
Label column = new Label(i, 0, model.getColumnName(i));
sheet1.addCell(column);
System.out.println(column.getContents());
}
int j = 0;
for (int i = 0; i < model.getRowCount(); i++) {
for (j = 0; j < model.getColumnCount(); j++) {
System.out.println(model.getRowCount());
System.out.println(model.getColumnCount());
if(model.getValueAt(i, j) == null){ //At this point I verify if the value is null
model.setValueAt("Nao possui", i, j);
}
Label row = new Label(j, i + 1, //I got NULL POINTER here
model.getValueAt(i, j).toString());
System.out.println(row.getContents());
sheet1.addCell(row);
}
}
workbookVazio.write();
workbookVazio.close();
Here is the code of my AbstractTableModel:
public class MacroTableModel extends AbstractTableModel {
private String[] colunas;
private List<Macro> linhas;
public MacroTableModel(List<Macro> lista){
this.colunas = new String[]{"VPN Name", "VCID", "VCID BACKUP"};
this.linhas = new ArrayList<Macro>(lista);
}
public String getColumnName(int index) {
return colunas[index];
}
public int getRowCount(){
return linhas.size();
}
public int getColumnCount(){
return colunas.length;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex){
Macro macro = new Macro();
switch(columnIndex){
case 0:
macro.setVpnName(aValue.toString());
break;
case 1:
macro.setVcid(aValue.toString());
break;
case 2:
macro.setVcid_BackUp(aValue.toString());
break;
}
fireTableCellUpdated(rowIndex,columnIndex);
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Macro macro = linhas.get(rowIndex);
switch(columnIndex){
case 0:
return macro.getVpnName();
case 1:
return macro.getVcid();
case 2:
return macro.getVcid_BackUp();
}
return null;
}
public void setColunas(String[] colunas) {
this.colunas = colunas;
}
public String getColunas(int i) {
return colunas[i];
}
}
When I debug the setValueAt method I get the correct value, but I still get the same error.
I might forgot some implementation in my AbstractModel class, I dont know exactly. Can someone help, please ?
The following two lines
public void setValueAt(Object aValue, int rowIndex, int columnIndex){
Macro macro = new Macro();
should be replaced by
public void setValueAt(Object aValue, int rowIndex, int columnIndex){
Macro macro = linhas.get(rowIndex);
Otherwise, you're modifying a new Macro that is not even part of the model, and this new Macro becomes eligible to GC right after the setValueAt() method returns. You want to change the value of the Macro that is in the model, at this row index.
That said, I find it a bit strange to modify the model when exporting it to Excel. Why doesn't the model do the substitution by itself:
public Object getValueAt(int rowIndex, int columnIndex) {
Macro macro = linhas.get(rowIndex);
switch(columnIndex){
case 0:
return valueOrDontHave(macro.getVpnName());
break;
case 1:
return valueOrDontHave(macro.getVcid());
case 2:
return valueOrDontHave(macro.getVcid_BackUp());
}
return null;
}
private valueOrDontHave(Object value) {
return value == null ? ""Nao possui" : value;
}

Categories