I have a JTable with my own Model (extends AbstractTableModel), and I'd like to add a row to it when I Click a JButton.
I don't really know how to hadd the row to the Model.
here is my model:
public class MembersModel extends AbstractTableModel {
String[] columnNames = {
"Name",
"Money Spent",
"Percent",
"Current Deck"
};
Object[][] data = {
{"Cajo", new Integer(150), new Integer(0), "Event Deck"},
{"Sekiam", new Integer(200), new Integer(0), "Jeskay"},
{"Nuvas", new Integer(100), new Integer(0), "Big Shit"},
{"Dos", new Integer(100), new Integer(0), "Crap Deck"},
{"Atoj", new Integer(100), new Integer(0), "IDK"}
};
public MembersModel(){
super();
calcAllPercent();
}
public void calcAllPercent(){
for(int i = 0; i < data.length; ++i){
data[i][2] = calcPercetage((Integer) data[i][1]);
}
}
private int calcPercetage(int money){
return (money*100)/teamMoneySpent();
}
private int teamMoneySpent(){
int money = 0;
for(int i = 0; i < data.length; ++i){
money += (Integer) data[i][1];
}
return money;
}
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
return false;
}
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
calcAllPercent();
fireTableRowsUpdated(0, 4);
}
}
Should i create my table aswell or add the method to the Model?
The key will be the data nucleus that you're using for your model, which here is your 2-D array, Object[][] data. The question then boils down to this: how do you add another row to the array and then notify the model's listener of the addition. While this can be done by creating a new data array with another row, copying all the data from this array, and adding the new data to the newly added row, why bother? For my money I'd
create a class to hold the data of a single table row, here I'll call it MyType, but you'll give it a better name, and it will have String, int, int, and String fields to correspond to the columns of your table.
Give this class some of the methods you have in your model above, such as doing row-specific calculations, calcPercentage(..), teamMoneySpent(...), and then the model can call the row object's method when this information is needed.
use an ArrayList<MyType> as my table model data nucleus, not a 2-dimensional hard-coded array.
give my model class an addRow(MyType myObj) method
in the method add to the ArrayList
and then call the appropriate model notification method, which here would be fireTableRowsInserted(...).
Note, I'm not sure what you mean by,
Should i create my table aswell or add the method to the Model?
Some problems with your current model:
The setValueAt() method is wrong. You should invoking tableCellUpdated(...). You don't want to repaint the data in the entire table when you update a single cell. Actually you don't even need to implement the setValueAt(...) method because your table is not editable.
You didn't override the getColumnClass(...) method. This method is required so the table can use the proper renderer to display the data.
Instead of creating a completely new TableModel, you could extend the DefaultTableModel. It already supports an addRow(...) method.
However, as hovercraft has already pointed out a better design is create a custom Object to store all the data of a single row. For a solution that uses this approach check out Row Table Model for a solution. The base TableModel is more complicated, but it makes it easier to create custom TableModels in the future since all the common code is in one class.
I would implement hovercraft's suggestion first so you better understand the concepts of creating a TableModel with a custom Object. I include this link as a suggestion for the future.
Related
my problem is that when I put Object to table in cell with CellEditor set to work as JComboBox and it's fine, but when click on the cell i got list with Objects, but selected one is not that which were in cell before, but just first on the list. Is there simple way to fix it?
public void setValueAt(Object value, int row, int col) {
data.get(row).values.set(col, (Device) value);
fireTableCellUpdated(row, col);
}
and
for(int i = 0; i < deviceTable.getModel().getColumnCount(); i++){
ExtendedAbstractTableModel model = (ExtendedAbstractTableModel) deviceTable.getModel();
JComboBox<Device> combo = new JComboBox<Device>();
for(Device value : model.columnsCombo.get(i)){
combo.addItem(value);
}
TableColumn columnModel = deviceTable.getColumnModel().getColumn(i);
columnModel.setCellEditor(new DefaultCellEditor(combo));
}
As shown in this example, DefaultCellEditor handles this for you. You're adding multiple instances in a loop; a single instance can handle the entire column. DefaultCellEditor works by overriding setValue() in the nested EditorDelegate. It's not clear how you've defeated this feature, but understanding the default may guide your search.
public void setValue(Object value) {
comboBox.setSelectedItem(value);
}
Finally I found what was wrong. I didn't override equal method in my class, and that is why these components couldnt recognise same item. Anyway, thank you all.
I have tried my best to look at all docs and javadoc for the RowFilter. I am using an array list of objects and I have a AbstractTableModel for my JTable. I want to filter the JTable to only show the object(s) date property where it is after a certain date.
Is the AbstractTableModel used as the argument to the includes method? This is the example from the documentation.
RowFilter<PersonModel,Integer> ageFilter = new RowFilter<PersonModel,Integer>() {
public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) {
PersonModel personModel = entry.getModel();
Person person = personModel.getPerson(entry.getIdentifier());
if (person.getAge() > 20) {
// Returning true indicates this row should be shown.
return true;
}
// Age is <= 20, don't show it.
return false;
}
};
Is it possible to get an explanation of this includes method and some way of adapting it to my situation. I understand how to compare dates, but how does the method access the row and object in my JTable?
Okay, it's really very simply...once you've done it few times ;)
RowFilter<PersonModel, Integer> ageFilter = new RowFilter<PersonModel, Integer>() {
Obviously defines the constraints of the RowFilter, basically, it defines the "row model" (PersonModel) and the method of referencing the each row (Integer)
Basically, it says, "I have a TableModel which can be accessed via a Integer value"...
public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) {
Is the only abstract method that needs to be implemented ;)
PersonModel personModel = entry.getModel();
Gets the model from the entry
Person person = personModel.getPerson(entry.getIdentifier());
Basically, this gets the Person from the model using the row index as specified by entry.getIdentifier()
if (person.getAge() > 20) {
// Returning true indicates this row should be shown.
return true;
}
// Age is <= 20, don't show it.
return false;
Okay, that's pretty basic ;)...
Now, without any more information to go about your specific implementation, this is a REALLY basic example of what you "might" be able to do...
To make life easier, I simply wrapped it all up in a simple class, basically, when needed, you would create a new instance, passing the Date value to it and then setting the table's RowSorter filter with it...
public class DateRowFilter exetends RowFilter<TableModel, Integer>() {
private Date date;
public DateRowFilter(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
public boolean include(Entry<? extends TableModel, ? extends Integer> entry) {
boolean include = false;
TableModel model = entry.getModel();
int row = entry.getIdentifier();
for (int col = 0; col < model.getColumnCount(); col++) {
Object value = model.getValueAt(row, col);
// Make decisions here
// You'll probably want to use Date#isBefore or Date#isAfter
// depending on which side you are comparing
}
return false;
}
}
I have a function called FetchInbox() which fetches the header information (Sender, subject, date sent) of an email and then adds it to a Vector of String Vectors.
What I want to be able to do is to refresh this table as new emails come in and update the table by first running FetchInbox() again, and then using this to repopulate the table.
I know this can be done using a TableModel, but I have yet to find a example which uses Vectors and not Object[][]. Any assistance with this would be appreciated.
DefaultTableModel has constructors and methods that take Vectors instead of Object[]s.
The old version of DefaultTableModel only used Vectors, the Object[] parameters are newer methods that were added around the time Generics came to Java.
http://docs.oracle.com/javase/tutorial/uiswing/components/table.html
When you create a table without providing it a model, it will have DefaultTableModel as it's default model. This model has two function:
setDataVector(Vector dataVector, Vector columnIdentifiers): Where dataVector is a Vector(which represents the data rows of table) of Vector and comlumnIdentifiers is Vector containing identifiers. It will show your table as you are providing the Vector.
addRow(Vector dataRow): it will add a data row to your dataVector as defined above.
So it is really simple to get the model and invoke these function:
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.setDataVector(dataVector, comlnIdentifiers);
In your context, dataVector has the type vector<vector<string> >. But depending on Vector is not really a good choice. It is much safer and effective if your directly work with Object[]. The DefaultTableModel has similar function with Object array too.
setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
addRow(Object[] rowData)
Check out the Tutorial page: How to Use Table to know many more things you can do with table and it's model.
This should work, but #jzd's answer is probably what you want, with the caveat that, according to the documentation, the column Vectors might be truncated or padded if their length does not match the number of columns you want in your table.
import javax.swing.*;
import javax.swing.table.*;
import java.util.*;
class test{
public static void main(String[] _) {
// Test data.
final Vector<Vector<String>> rows = new Vector<Vector<String>>();
for (int i = 0; i < 4; i++) {
Vector<String> row = new Vector<String>();
for (int j = 0; j < 5; j++) {
row.add(String.format("%s, %s", i, j));
}
rows.add(row);
}
// With AbstractTableModel, you only need to implement three methods.
TableModel model = new AbstractTableModel() {
public int getRowCount() {
return rows.size();
}
public int getColumnCount() {
return rows.elementAt(0).size();
}
public Object getValueAt(int row, int column) {
return rows.elementAt(row).elementAt(column);
}
};
// Test the TableModel in a JTable.
JFrame jf = new JFrame("test");
jf.setSize(512, 384);
jf.setContentPane(new JScrollPane(new JTable(model)));
jf.show();
}
}
have a look at GlazedLists - it'll save you a ton of time.
with it you can dynamically bind a JTable to a List of objects such that any change in the objects is reflected in the table and vice-versa.
I have 6 columns and a maximum of about 20 rows in the database. I like to make a general program in Java that populates the JTable with these values. I am very much conversant with using dynamic arrays, but not familiar with JTable attributes and model in Swing.
Can anyone guide me here on tho requirement? Thanks
You have to create your own table model. See the API documentation here: http://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableModel.html
Basically, getColumnCount() would return your number of columns, getRowCount() the number of rows in your database, getColumnName(int columnIndex) some name for every column (the column name from the database or an arbitrary name, maybe from a constant string array). getColumnClass(int columnIndex) can, in the simple case, return String.class for every column. Then you have to convert every value to string.
getValueAt(int rowIndex, int columnIndex) has to return the value from the database for the given row and column. You should probably pre-load all these values in a 2D array or something like this (but that would be the answer to another question ;) ).
You can ignore the other methods for now, they are for editable tables.
Your code could look something like this:
String[][] tableData = readTableDataFromDatabase();
String columnNames = initColumnNames(); //From DB or constants
TableModel model = new DbTableModel(tableData, columnNames);
class DbTableModel extends AbstractTableModel {
private String[][] tableData;
private String[] columnNames;
public DbTableModel(String[][] tableData, columnNames) {
this.tableData = tableData;
this.columnNames = columnNames;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return tableData[rowIndex][columnIndex];
}
#Override
public int getRowCount() {
return tableData.length;
}
#Override
public int getColumnCount() {
if(tableData.length == 0)
return 0;
return tableData[0].length;
}
#Override
public String getColumnName(int column) {
return columnNames[column]
}
}
This example assumes that you read the data from the database as a 2D array. If you use an O/R-Mapper (like the Java Persistence API - JPA), which I highly recommend, you would probably load a list of Entities. Each entity would contain the data for a table row. Anyway, the table model would not change much. Instead of accessing array values, you would get an entity object from the list and call "get"-Methods on the Entity.
There's more information in the Java tutorial: http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data
Nice but i hope readTableDataFromDatabase(); return resultset object
Have a look at the tutorial here: http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data
Here's a sample of a List-based read-only TableModel I wrote some time ago:
https://sourceforge.net/p/puces-samples/code/HEAD/tree/tags/sessionstate-1.0/sessionstate-suite/sessionstate-sample/src/blogspot/puce/sessionstate/sample/ParticipantTableModel.java
I have a JXTable with custom table model. I added 2 ColorHighlighter's with custom HighlightPredicate's.
Problem is when i click on the column header, the table sorts the rows, BUT the highlighter's remain as for the old view.
How can I update the state of the highlight after sorting a table?
As #kleopatra mentioned, i looked at my predicate:
HighlightPredicate spakowany = new HighlightPredicate() {
#Override
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
TableModel tableModel = table.getModel();
if (tableModel instanceof StanTableModel) {
StanTableModel stanTableModel = (StanTableModel) tableModel;
// int rowIndex = adapter.row; <- this was the issue
int rowIndex = adapter.convertRowIndexToModel(adapter.row);
StanTableRow myCustomRow = stanTableModel.getRow(rowIndex);
if ((myCustomRow.isSpakowany()) {
return true;
}
}
return false;
}
};
and used #mKorbel idea:
was:
int rowIndex = adapter.row;
is now:
int rowIndex = adapter.convertRowIndexToModel(adapter.row);
And it works now.
StanTableModel is my custom table model. It has getRow() function and returns a StanTableRow object which in turn has isSpakowany() function.