remove or Add rows from JTable with AbstractTableModel - java

ive been trying to update the table after the insertion or deletion of items from a abstract table model but whenever i do that, instead of removing the old records and replace with the new ones, the old rows remains and it creates all the rows again without removing the old ones..so i get duplicate items, this is the code im using :
for the data inserted :
TestModel tm = new TestModel() ;
tm.fireTableRowsInserted(records.length, records.length);
and for the data deleted :
TestModel tm = new TestModel() ;
tm.fireTableRowsDeleted(records.length, records.length);
any clue of how to get around with that?
any help is greatly appreciated!
Kind regards,
Romulo Romero

Create a table with a boolean column. Since using this boolean column you can delete those rows that are selected for deletion. Just like the following screen shot,
Then in your TableModel make a List<StudentDO> such that it will hold all the table data.
Adding a Row:
To add a row just create a new StudentDO and send it to the table model and the model addRow method will add the object to the table list.
Deleting Rows:
For Deleting rows just call a delete method and this should fire event in TableModel such that model should traverse all the rows and check which ever row is selected and delete it.
Note: Deleting rows should be done from the end not from the beginning of the list.
StudentTableModel.java
class StudentTableModel {
// Required methods code goes here.
public void addRow(StudentDO do1) {
data.add(do1);
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
}
public void deleteRow() {
for(int rowIndex = data.size() - 1; rowIndex >= 0; rowIndex--) {
if(data.get(rowIndex).isSelect()) {
data.remove(rowIndex);
}
}
fireTableDataChanged();
}
}
P.S: fireXXXMethods should be called only in the model. Because any data change will be responsible of the model.

Related

Java: Refreshing my jTable after data updates in child jDialog

I've investigated lots of different questions and answers around this, but can't find one that seems to work.
I'm new to Java, but have experience in a variety of different languages and, so far (in context to what I'm experimenting with), it's feeling a bit like VBA except with having to build up the actions/functions that you feel should be already there. This is, I expect, just down to my own inexperience though.
I'm using Netbeans IDE 8.2 with Java Runtime 1.80.
I have created jFrame that contains a jTable. The jTable is built with data like so:
public void showTasks(Boolean removeID) {
ArrayList<Tasks> list = tasksList("SELECT * FROM tasks");
JTable table = jTable1;
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
model.addRow(row);
}
// Remove the 'id' column from the table view
if(removeID) { table.removeColumn(table.getColumnModel().getColumn(0)); }
}
The background behind this is less relevant, but essentially tasksList is a function that applies the query to an SQL statement, returning an ArrayList. I build up my rows with 4 columns, then remove the first column so 'ID' is available but not visible (this final action has been segregated through testing/experimentation).
I have another area of code that opens a jDialog when a row is clicked, in which it is possible to update the MySQL DB.
Problem
I'm trying to throw in a function call so that the table data 'refreshes' when the jDialog is closed. I have temporarily added in a button to the jFrame (where the jTable lives) to test/debug this function.
I can't seem to get this to work, though. The closest I have achieved is to re-call showTasks(false), but this obvious just adds rows with updated data, rather than replacing the dataset. I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
As I'm new to Java, and may still be looking at it from a flawed method of thinking, I'm finding it difficult to apply any other examples to that of my own. I also can't seem to find a way to implement fireTableDataChanged().
Surely this is a simple concept I'm over-thinking?
Edit - based on below answer
Is there a reason why something like this would be considered incorrect, if deleting all rows and adding them back in is okay?
public void refreshTasks() {
DefaultTableModel model = (DefaultTableModel) jTable1.getModel();
int row_total = model.getRowCount();
for(int i= row_total -1;i>=0;i--) {
model.removeRow(i);
}
showTasks(false);
}
Edit: Button to invoke data update
Now works correctly (if not improperly) with the following:
private DefaultTableModel parentTable; // To store the parent 'Task' table model
public void setStart(int user,DefaultTableModel table) {
this.txt_taskID.setText(Integer.toString(user)); // Converts the ID to a string
addData(user); // Populates the fields
parentTable = table; // Sets parent TableModel to a variable
}
The above code is called from the Parent jFrame when the Dialog is opened, and passes the Table model and the 'ID' of the row I'm looking to edit. The table model is stored in parentTable.
There's also a 'Save' button, and a 'Cancel' button. I'm yet to separate these, and currently 'Save' does just that (SQL update and so on). My 'Cancel' button closes the dialog and refreshes the jTable, as per the below function:
private void btn_CancelActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false); // Hide the dialog
Menu menu = new Menu(); // for accessing the tasksList function
parentTable.setRowCount(0); // Clears the jTable data
// jTable data is then 'rebuilt' using the new data
ArrayList<Tasks> list = menu.tasksList("SELECT * FROM tasks");
Object[] row = new Object[4];
for(int i=0;i<list.size();i++) {
row[0]=list.get(i).getId();
row[1]=list.get(i).getName();
row[2]=list.get(i).getDesc();
row[3]=list.get(i).getDate();
parentTable.addRow(row);
}
}
I'm not 100% sure if deleting all the rows, then building them back in is 'best practice'.
Yes that is probably the best practice.
The only other approach is to create a completely new TableModel and add it to the table using the setModel() method. The problem with this approach is that it will reset any custom renderers/editors you may have set on the table.
The easiest way to remove all the rows from the DefaultTableModel is to just use:
model.setRowCount(0);
I'm not sure how you want to do it but I'm gonna give you simple example for deleting and refreshing JTable maybe it's help you.
This following btnDelete Jbutton added to JFrame for deleting rows from table:
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowIndex = table.getSelectedRow();
if(rowIndex > -1) {
int x = (Integer) table.getModel().getValueAt(rowIndex, 0);
if (conn.removeContact(x) == true) { //here add your own code like removeID
model.removeRow(rowIndex);
lblInfo.setText("Contact deleted successfully.");
model.fireTableDataChanged();
} else {
lblInfo.setText("Cannot remove at this time!");
}
}else {
lblInfo.setText("At first you need select a row with single click!");
return;
}
}
});
and these following codes for refreshing table in primitive way :
btnRefresh.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int rowCount = model.getRowCount();
for (int i = rowCount - 1; i >= 0; i--) {//remove all rows
model.removeRow(i);
}
lblInfo.setText("Table datas updated successfully.");
for (Person p : conn.readAllContacts()) {//add all row from scratch
model.addRow(new Object[] { p.getId(), p.getName(), p.getLastName(), p.getPhone(), p.getEmail() });
}
}
});

How to extract named column from tablemodel in java swing

I have a table that shows the results of user defined queries in a swing project. I want to allow the user to extract the data from a particular names column, if present. At the moment I can select the data from a column when I click the column but I don't know how to do the same thing from a button so that only a particular column's data (the column is called HNum) is obtainable. The code I have so far is as follows. If this is impossible I could always try to make sure that HNum is the first column but I need something cleaner I think.
btnCompare.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object[] data_L = columnToArray(table,table.getSelectedColumn());
}
}
public Object[] columnToArray(JTable table, int columnIndex){
// get the row count
int rowCount = table.getModel().getRowCount();
// declare the array
Object [] data = new Object[rowCount];
// fetch the data
for(int i = 0; i < rowCount; i++){
data[i] = table.getModel().getValueAt(i, columnIndex);
}
return(data);
}
Do you tried to use a TableColumnModel ?
You can define all the treatment you need like a getColumnName{}
https://docs.oracle.com/javase/7/docs/api/javax/swing/table/TableColumnModel.html
edit :
an example
http://www.java2s.com/Tutorial/Java/0240__Swing/ExtendingAbstractTableModel.htm

Swing - invisible column throwing exception when accessing

Background
I have a JTable called table, and I have a column that is not part of the DefaultTableModel so its invisible:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist",
"Album", "Time"}, 0)
I add the respective rows like this:
int upTo = songList.size();
int idx = 0;
while (idx < upTo) {
SongObject curSong = songList.get(idx);
model.addRow(new Object[]{
curSong.toString(),
curSong.getArtist(),
"-",
curSong.getDuration(),
curSong});
idx++;
}
Where curSong is the the current song object that it is adding, the SongObject contains all data about the song. The toString() returns the title of the song.
Problem:
The problem is that when I try to access the column like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.getSelectedRow(), 4);
It throws a java.lang.ArrayIndexOutOfBoundsException: 4 >= 4 exception.
Can anyone explain why and propose a solution?
Thanks in advance :)
DefaultTableModel.addRow() somewhere down the chain executes private justifyRows() method, which trims the unused columns from the row to the size equal to getColumnCount(). So the fifth column is never added to the model. As a result, you get ArrayIndexOutOfBoundsException when you're attempting to access this column.
If you need access to the actual SongObject then you can have a custom model that would return SongObject for a given row index. Make an extension of AbstractTableModel. See How to Use Tables tutorial for examples.
As an alternative, you can still use SongObject in a visible column. Just use a custom renderder that would renderer SongObject as a string for example. See Using Custom Renderers for details. You can reuse DefaultTableModel in this case.
Thanks to Aqua I overrode the following:
final JTable table = new JTable(new DefaultTableModel(new Object[]{"Title", "Artist", "Album", "Time"}, 0) {
#Override
public void addRow(Object[] rowData) {
Vector blah = DefaultTableModel.convertToVector(rowData);
insertRow(super.getRowCount(), blah);
}
#Override
public void insertRow(int row, Vector data) {
super.dataVector.insertElementAt(data, row);
super.fireTableRowsInserted(row, row);
}
});
Then I accessed the item in the fifth column (which is not part of the model!) like this:
SongObject songToPlay = (SongObject) table.getModel().getValueAt(table.convertRowIndexToModel(
table.getSelectedRow()), 4); //get the value at the VIEW location NOT THE MODEL collection
Sorry for the messy code but it worked. Home I could help someone with a similar problem. The solution just misses the justifyRows() method found in DefaultTableModel

Null error when attempting to add custom row to CellTable in gwt

I have a Cell Table that I am using to output some search results. The cell table uses a list data provider to update info. I want to separate different sections so I am attempting to add a custom row in between different sections that has one cell that spans all of the columns. I am extending AbstractCellTableBuilder to do this, but my issue comes when I use TableRowBuilder and startRow(), calling startRow() returns a null pointer exception, to AbstractCellTableBuilder.java:243, which refers to tbody. So this is leading me to believe that my cell table is not getting passed into AbstractCellTableBuilder properly. My understanding of gwt and java is pretty basic, so I might just not be understanding how exactly this is supposed to work, and the showcase example is pretty complicated for me to understand. If anyone can tell where I'm messing up or has any simpler examples of this that might help me I would appreciate it!
I had found a similar answer and tried to implement it, and that is how I came up with what I have, but it answer wasn't quite detailed enough for me to fully understand how it works. Here is what I referenced:
Building a custom row on demand with GWT CellTableBuilder
EDITED:
Basic format of how I add normal rows to the cell table
searchProvider = new ListDataProvider<SearchColumn>();
cellTable_2 = new CellTable<SearchColumn>();
//Add columns to the cellTable
searchProvider.addDataDisplay(cellTable_2);
//What I call when adding a row to the cellTable using the ListDataProvider
searchProvider.getList().add(new SearchColumn("",label,"","","","","","",""));
Adding the CustomCellTableBuilder to the cell table:
//Passing the CustomCellTableBuilder to the cell table
CustomCellTableBuilder buildRow = new CustomCellTableBuilder();
cellTable_2.setTableBuilder(buildRow);
The CustomCellTableBuilder for adding custom rows:
public class CustomCellTableBuilder extends AbstractCellTableBuilder<SearchColumn>{
public CustomCellTableBuilder() {
super(cellTable_2);
}
#Override
protected void buildRowImpl(SearchColumn rowValue, int absRowIndex){
//building main rows logic
if (labelrow == 1){
System.out.println("Going to build extra row if");
buildExtraRow(absRowIndex, rowValue);
}
else {
System.out.println("Getting into normal buildRow");
buildRow(rowValue,absRowIndex);
}
}
private void buildExtraRow(int absRowIndex, SearchColumn rowValue){
start(true);
TableRowBuilder row = startRow();
TableCellBuilder td = row.startTD().colSpan(getColumns().size());
td.text("Testing this out").endTD();
row.endTR();
}}
I think you should call start(true) before calling startRow() because tbody is initialized to null. Start() call will initialize tbody to HtmlBuilderFactory.get().createTBodyBuilder().
The source doesn't lie.
Just like that:
private void buildExtraRow(int absRowIndex, SearchColumn rowValue) {
start(true); // true makes builder to rebuild all rows
TableRowBuilder row = startRow();
// whatever
}

Updating the content of a JTable

I have a JTable which displays an Object[][] of data.
There is also a form on the same screen that lets the user add an item to the object to the list. Although for the life of me I can not get the list displayed in the table to update when the user presses the "Add Item" button.
(it gets appended to the array fine, and I can print it onto the screen, just can't get the table to change.)
Here is crating of the table
Object[] tableHeadings = {"Item Name","Qty/Weight","Price"};
ShoppingApplication shoppingApplication = new ShoppingApplication();
Object[][] tableData = shoppingApplication.generatePurchaseTableData();
final JTable tblBill = new JTable(tableData, tableHeadings);
Here is the table data being generated:
/**
* Generates data in the correct format to go into the table
* from the purchase list
* #return
*/
public Object[][] generatePurchaseTableData(){
Object[][] results = new Object[listOfPurchases.size()][2];
for (int i = 0; i<listOfPurchases.size(); i++){
Purchase purchase = listOfPurchases.get(i);
Object[] innerObject =
{purchase.getProductName(),
purchase.getPricePerUnit(),
purchase.getTotalPrice()};
results[i] = innerObject;
}
System.out.println(results.length);
return results;
}
}
Here's the action listener
/* Add Action Listeners */
cmdAddItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
AddPurchase addPurchase = new AddPurchase(
(radWeight.isSelected()) ? 'w' :(radQty.isSelected()) ? 'q' : 'u',
txtNameOfItem.getText(),
txtAmount.getText(),
txtUnits.getText(),
txtPricePerUnit.getText());
ShoppingApplication sa = new ShoppingApplication();
Object[][] newData = sa.generatePurchaseTableData();
//TODO make tblBill updata it's contents
}
});
From the code above, it does look like I haven't made much effort, but I've actually been trying to get this to work for hours now, tried using different methods, data structures and been getting no where to went right back to the start, and that is what the above code it.
I've Googled this a bit, and can't seem to find anything, can't think why no one else has seemed to get stuck on this. (maybe I'm just being thick) but hopefully this post will help others too.
Thanks in advance :)
The JTable has a data model (TableModel), which either holds the values, or is an adapter to the actual data.
Data in a table can be update by either changing the data in the table model, or by setting a new table model.
In your case, you could use the javax.swing.table.DefaultTableModel:
TableModel tableModel=new DefaultTableModel(newData, tableHeadings);
tblBill.setModel(tableModel);
update when the user presses the "Add Item" button.
Why do recreate the entire table when you only add a single item? That is not very efficient.
Instead must add a new row to the table. This code (like the above code) assumes you are using the DefaultTableModel:
DefaultTableModel model = (DefaultTableModel)table.getModel();
model.addRow(...);

Categories

Resources