How to update the view of JTable after adding a new row? - java

This is my TableModel, I have extended AbstractTableModel
class CustomTableModel extends AbstractTableModel
{
String[] columnNames = {"Name","Contact","eMail","Address","City","Pin","State","Type","ID"};
Vector<String[]> data = new Vector<String[]>();
CustomTableModel()
{
try
{
//Using JDBC connection//
while(rs.next())
{
String[] s=new String[9];
s[0]=rs.getString(1);
//System.out.println(s[0]);
s[1]=rs.getString(2);
s[2]=rs.getString(3);
s[3]=rs.getString(4);
s[4]=rs.getString(5);
s[5]=rs.getString(6);
s[6]=rs.getString(7);
s[7]=rs.getString(8);
s[8]=rs.getString(9);
data.add(s);
}
}
catch(Exception e)
{
System.out.println("the exception is :"+e.toString());
}
}
public int getColumnCount() {
int columnCount = columnNames.length;
return columnCount;
}
public int getRowCount() {
int rowCount = data.size();
return rowCount;
}
public Object getValueAt(int rowIndex, int columnIndex) {
return data.get(rowIndex)[columnIndex];
}
public String getColumnName(int column) {
return columnNames[column];
}
public void removeRow(int r)
{
for(int i=0;i<data.size();i++)
{
String[] s = (String[])data.get(i);
if(s[0]==getValueAt(r,0))
{
try
{
//using JDBC connections to delete the data from DB//
//also removing the value from data and also updating the view//
data.remove(data.get(i));
fireTableRowsDeleted(r, r);
}
catch (Exception e)
{
System.out.println(e.toString());
}
break;
}
}
}
//I am using the following code to update the view but it doesnot work//
public void addRow(String[] a)
{
data.add(a);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
}
I have a table class which extends CustomTableModel .
class table extends CustomTableModel
{
final JButton editButton = new JButton("Edit");
final JButton deleteButton = new JButton("Delete");
final JTable mytable = new JTable(new CustomTableModel());
.
.
.
}
I have a add button , and in its action listener i use the following code to pass the values that i wanted to add.
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
Pls let me know where i am going wrong . Thanks

Pls let me know where i am going wrong . Thanks
String[] a = {"a","b","c","d","e","f","g","h","i"};
table myTableObj = new table();
myTableObj.addRow(a);
code lines talking about
create a new row
create a new JTable
row is added to a new JTable
result is that a new JTable is never added to visible Swing GUI
don't do that, why is a new JTable recreated on every JButtons event
add String[] a... to the CustomTableModel directly
for better help sooner post an SSCCE, short, runnable, compilable

The table class makes no sense. It is supposed to be a TableModel that shoud be set into a JTable. Instead you have JTable declared as a field inside this table class (which should be Table btw according to Java naming convention). The result is that when constructing a new table object, a JTable is constructed inside it with another CustomTableModel inside. So the tableModel you are adding rows into is not the tableModel actually used by your JTable.

You can also use the myCustomTable.fireTableStructureChanged();

Related

empty TableModel after instantiating

Newbie seeking help please :-)
I am working on a little project to get familiar with Java desktop development and Database connectivity.
Attached code gives me an empty TableModel after instantiating therefore no data displayed in the JFrame.
Test class is instantiated from the menue of the main window with Test.showFrame();.
package ...
import ...
public class Test extends JPanel {
public Test() {
initializePanel();
}
private void initializePanel() {
// Creates an instance of TableModel
CategoryTableModel tableModel = new CategoryTableModel();
System.out.println(tableModel.getRowCount());
// Creates an instance of JTable with a TableModel
// as the constructor parameters.
JTable table = new JTable(tableModel);
table.setFillsViewportHeight(true);
JScrollPane scrollPane = new JScrollPane(table);
this.setPreferredSize(new Dimension(500, 200));
this.setLayout(new BorderLayout());
this.add(scrollPane, BorderLayout.CENTER);
}
public static void showFrame() {
JPanel panel = new Test();
panel.setOpaque(true);
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
class CategoryTableModel extends AbstractTableModel {
private List<Category> all = null;
private Iterator<Category> iterator = null;
private int tableRowCount;
private TableModel tableModel;
public CategoryTableModel(){
Vector tableData = new Vector();
// TableModel's column names
Vector<String> tableHeaders = new Vector<String>();
tableHeaders.add("Category");
// Database call
all = new ReadCategory().allCategories();
// TableModel's data
for(Object o : all) {
Vector<Object> row = new Vector<Object>();
all.forEach((n) -> row.add(new Category().getName()));
tableData.add(row);
System.out.println("row added");
}
tableRowCount = tableData.size();
tableModel = new DefaultTableModel(tableData, tableHeaders);
System.out.println(tableModel.getRowCount());
}
#Override
public int getRowCount() {
return 0;
}
#Override
public int getColumnCount() {
return 0;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return null;
}
}
}
Database call is fetching data via Hibernate and stores data in a .
Thanks for help.
In its most basic form a table model for a JTable defines the columns, the mapping of object to column and holds the data for the JTable to call upon. If we take your current table model and cut it down to fit this basic requirement we get the following.
import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public class CategoryTableModel extends AbstractTableModel {
private final List<Category> tableData = new ArrayList<>();
public void add(Category cat) {
tableData.add(cat);
fireTableDataChanged();
}
#Override
public String getColumnName(int column) {
String result = "";
if (column == 0) {
result = "Category Name";
}
return result;
}
#Override
public int getRowCount() {
return tableData.size();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return tableData.get(rowIndex).getName();
}
return null;
}
}
Notice that we do not define any data in the model itself. All we define is some storage for the data and the column name of the single column that we require.
I have added an add() method that allows you to add data to the model (you may wish to define remove etc. yourself). When you add or remove data from the model you must always let the JTable know that the data has changed by calling fireTableDataChanged() so that the table can redraw itself.
To use this table model you will need to do
CategoryTableModel model = new CategoryTableModel();
model.add(new Category());
JTable table = new JTable(model);
You can replace the model.add() with a loop that iterates over your data and adds it to the model.

Is there a way to put an ArrayList into a JTable where each line is the next index of the ArrayList?

I have many different array lists. I want each index to be a new row in the JTable but I'm not sure how to do that. I made a for loop but it is not working.
Is there even a way to populate a JTable with an array list and not an array?
public TableCreator() {
super(new GridLayout(1,0));
String[] columnNames = {"Item Type",
"Description",
"Size",
"Price"};
// for(int i=0; i<ShoppingFunctions.cartType.size(); i++){
for(int i=0; i<GUI.size.size(); i++){//FIX!!!!
item = ShoppingFunctions.cartType.get(i)+"\n";
described = GUI.describe[GUI.imageNum];
sizes = GUI.size.get(i);
price = ShoppingFunctions.cartPrice.get(i)+"\n";
}//end of for
Object[][] data = {{item, described, sizes, price}};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setFillsViewportHeight(true);
if (DEBUG){
table.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
printDebugData(table);
}//end of method
});//end of listener
}//end of if
JScrollPane scrollPane = new JScrollPane(table);
add(scrollPane);
}//end of method
The simple answer is to create your own, based on something like AbstractTableModel
For example...
public abstract class AbstractGenericTableModel<R> extends AbstractTableModel {
protected ArrayList<R> rows;
protected List columnIdentifiers;
public AbstractGenericTableModel() {
this(0, 0);
}
public AbstractGenericTableModel(int rowCount, int columnCount) {
this(new ArrayList(columnCount), rowCount);
}
public AbstractGenericTableModel(List columnNames, int rowCount) {
setData(new ArrayList<>(rowCount), columnNames);
}
public AbstractGenericTableModel(List<R> data, List columnNames) {
setData(data, columnNames);
}
public List<R> getRowData() {
return rows;
}
private List nonNullVector(List v) {
return (v != null) ? v : new ArrayList();
}
public void setData(List<R> data, List columnIdentifiers) {
this.rows = new ArrayList<>(nonNullVector(data));
this.columnIdentifiers = nonNullVector(columnIdentifiers);
fireTableStructureChanged();
}
public void addRow(R rowData) {
insertRow(getRowCount(), rowData);
}
public void insertRow(int row, R rowData) {
rows.add(row, rowData);
fireTableRowsInserted(row, row);
}
/**
* Removes the row at <code>row</code> from the model. Notification of the row being removed will be sent to all the listeners.
*
* #param row the row index of the row to be removed
* #exception ArrayIndexOutOfBoundsException if the row was invalid
*/
public void removeRow(int row) {
rows.remove(row);
fireTableRowsDeleted(row, row);
}
public void setColumnIdentifiers(List columnIdentifiers) {
setData(rows, columnIdentifiers);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return columnIdentifiers.size();
}
#Override
public String getColumnName(int column) {
Object id = null;
// This test is to cover the case when
// getColumnCount has been subclassed by mistake ...
if (column < columnIdentifiers.size() && (column >= 0)) {
id = columnIdentifiers.get(column);
}
return (id == null) ? super.getColumnName(column)
: id.toString();
}
}
public class ArrayListTableModel extends AbstractGenericTableModel<ArrayList> {
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
List<ArrayList> rows = getRowData();
return rows.get(rowIndex).get(columnIndex);
}
}
This creates two classes, an "abstract generics" based model, which allows you to specify the physical data which backs a row and a ArrayListTableModel which allows you to use a ArrayList for the individual data.
What this assumes though, is each row has the same number of elements, but it makes no checks
I suggest you have a closer look at How to Use Tables for more details
You can use the List Table Model. It will support rows of data stored in an ArrayList, Vector or any other class that implements the List interface.
The ListTableModel is also based on a generic TableModel that allows you to add Objects to a row in the model. There are many row based methods that allow easy usage of the model
The ListTableModel provides methods that allow you to easily customize the model:
setColumnClass – specify the class of individual columns so the proper renderer/editor can be used by the table.
setModelEditable – specify editable property for the entire model.
setColumnEditable – specify editable property at a column level. This property will have preference over the model editable property.
The other option is to iterate through the ArrayList and copy each row of data to a Vector and then add the Vector to the DefaultTableModel using the addRow(...) method.

How to refresh JTable after inserting data to database?

I'm populating JTable from access database. when running the code for the first time, table loads perfectly. Then adding new records to database from JDialog. What I was trying to do is to call loadData() method when JDialog is closed, but table is not updating.
This is my loadData() method:
private void loadData() {
System.out.println("sssss");
final String [] columnNames={"Seq", "First Name", "Last Name","Num1","Num2","Num3"};
connectDb();
data = new Object[rows][columns];
int row = 0;
try {
while(rs.next()){
for(int col = 0 ; col<columns; col++ ){
if(col==0)
data[row][col]=rs.getString("contact_seq");
if(col==1)
data[row][col]=rs.getString("contact_fname");
if(col==2)
data[row][col]=rs.getString("contact_lname");
if(col==3)
data[row][col]=rs.getString("contact_num1");
if(col==4)
data[row][col]=rs.getString("contact_num2");
if(col==5)
data[row][col]=rs.getString("contact_num3");
}
row++;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
model = new DefaultTableModel(data, columnNames){
/**
*
*/
private static final long serialVersionUID = 1L;
public boolean isCellEditable(int row, int column)
{
return false;
}
};
table = new JTable(model);
}`
this how I call loadData method when closing the JDialog.
JMenuItem mntmNew = new JMenuItem("New Contact");
mntmNew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addData gui = new addData(viewData.this,rs);
gui.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
gui.setVisible(true);
gui.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e){
loadData();
}
});
}
});
mnFile.add(mntmNew);
My database is updated when adding the records but Jtable is not refreshed.
Here:
private void loadData() {
...
table = new JTable(model); // don't re-create the table here
}
Don't re-create the table but update its model instead, either by setting a new table model or by clearing and re-filling the current one:
private void loadData() {
...
table.setModel(model);
// Of course, table should have been initialized
// and placed first, and only once!
}
See examples here (includes SwingWorker to make database calls in a background thread), here and here. Please have a look to those answers, there are explanations to make the point clear.
This worked for me:
if (model.getRowCount() > 0) {
for (int i = model.getRowCount() - 1; i > -1; i--) {
model.removeRow(i);
}
}
setTablevalue();
I removed all the rows from the JTable and again called the setTableValue method to re-populate the table.
This is a shot in the dark, but maybe this will work?:
public void windowClosed(WindowEvent e) {
loadData();
// Add this line:
table.repaint();
}
If I understand what is going on, the underlying database is getting updated but the JTable component is not showing the updates. My guess is that you just have to call the repaint() method so that the JTable gets updated as well.

JTable headers doesn't set

Last two days I've been trying to create a data view table in java app with JTable.
Netbeans binding option doesn't work, but nevermind, I managed to create my own TableModel.
Data get shown in the table, but the headers always contain just letters (A, B, C... for each column). At one point everything worked well, but then I wanted to set another model for that particular table and it doesn't show the labels correctly anymore even if I create whole new JPanel and set it up from scratch.
This is my custom TableModel class
public class MyTableModel extends AbstractTableModel{
private ArrayList<Options> list;
String[] headers;
public MyTableModel(ArrayList<Options> list, String[] headers) {
this.list = list;
this.headers = headers;
}
#Override
public int getRowCount() {
return this.list.size();
}
#Override
public int getColumnCount() {
return headers.length;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if(columnIndex == 0) {
return list.get(rowIndex).getId();
}
if(columnIndex == 1) {
return list.get(rowIndex).getText();
}
else {
return null;
}
} }
And this is part of the code in jframe class(the one which I run)
ArrayList<Options> list;
String[] optionHeaders = {"id", "text"};
public table2frame() {
initComponents();
list = (ArrayList) zadanie_2_app.Zadanie_2_app.findAll();
JTable table2 = new JTable(new MyTableModel(list, optionHeaders));}
AbstractTableModel requires that getColumnName be overridden otherwise placeholder column names are used. Add
#Override
public String getColumnName(int column) {
return headers[column];
}
I wanted to set another model for that particular table and it doesn't show the labels correctly anymore even if I create whole new JPanel and set it up from scratch.
AFAIK understand from this desription, that JTable is container for JTable
put JTable to the JScrollPane, JTable should be placed into JScrollPane, otherwise JTableHeader isn't visible automatically
get JTableHeader from JTable, change LayoutManager for JPanel to BorderLayout, put JTable to CENTER area, JTableHeader to NOTHR area

Remove JTable row that read file records

I am New in java, I have a JTable that can read records from a txt file and show they perfectly.
I want to add a new book to my JFrame that when user select a row on table and clicked the "delete" button, that row should delete and that deleted row records must delete from txt file,too.
my code is this, but it has errors and not seen JTable! :
public class CopyOfAllUserTable extends AbstractTableModel {
Vector data;
Vector column;
public static void main(String[] args){
new CopyOfAllUserTable();
}
public CopyOfAllUserTable() {
String line;
data = new Vector();
column = new Vector();
try {
FileInputStream fis = new FileInputStream("D:\\AllUserRecords.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
StringTokenizer st1 = new StringTokenizer(br.readLine(), " ");
while (st1.hasMoreTokens())
column.addElement(st1.nextToken());
while ((line = br.readLine()) != null) {
StringTokenizer st2 = new StringTokenizer(line, " ");
while (st2.hasMoreTokens())
data.addElement(st2.nextToken());
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
final JFrame frame1=new JFrame();
JTable table=new JTable(data,column);
JButton button1=new JButton("Delete");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultTableModel model=new DefaultTableModel(data, column);
JTable table=new JTable(model);
}
});
JPanel panel=new JPanel();
panel.add(table);
panel.add(button1);
frame1.add(panel);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setBounds(200, 80, 600, 500);
frame1.setVisible(true);
frame1.setEnabled(true);
}
public int getRowCount() {
return data.size() / getColumnCount();
}
public int getColumnCount() {
return column.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (String) data.elementAt((rowIndex * getColumnCount())
+ columnIndex);
}
}
My problem is in delete row, and read records from file to jtable are perfectly successful.
Firstly you're not adding your JTable to the content of the frame.
For containers like: frame.getContentPane() and JPanel you should add the child components by using their #add(...) method.
For example:
final JPanel panel=new JPanel(new BorderLayout());
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultTableModel model=new DefaultTableModel(data, column);
JTable table=new JTable(model);
panel.add(new JScrollPane(table));
panel.revalidate();
}
});
Note that JPanel default layout is FlowLayout. Second thing - if you want to have headers and scrolling in your JTable you need to wrap it with JScrollPane.
Next - you should revalidate the panel after adding/removing/etc.
The second issue is removing rows from JTable. I usually write a method to handle it:
protected void removeRows(final int[] rows) {
int modelRows[] = new int[rows.length];
for(int i = 0; i < rows.length; ++i) {
modelRows[i] = table.convertRowIndexToModel(rows[i]);
}
Arrays.sort(modelRows);
for(int i = modelRows.length - 1; i >= 0; --i) {
int row = modelRows[i];
model.removeRow(row);
}
model.fireTableDataChanged();
}
The convertRowIndexToModel method converts index returned by JTable#getSelectedRows() or JTable#getSelectedRow() (which are the visible indices) to the model indices. If you set RowSorter for your JTable or you leave it to standard implementation:
table.setAutoCreateRowSorter(true);
You are adding table directly to the panel with out using the JScrollPane. Your table header will not be visible if you do like this,
So instead of this,
JPanel panel=new JPanel();
panel.add(table);
Do this,
JPanel panel=new JPanel();
panel.add(new JScrollPane(table));
Why to use JScrollPane? Read this.
When user selects a row and clicks on delete, then get the selected row and remove it from the table list. As you are using AbstractTableModel then you have to write your custom removeRow(..) method to perform this.
Example:
private boolean removeSelectedRow(int row) {
// Remove the row from the list that the table is using.
dataList.remove(row);
// You need to call fireXXX method to refresh the table model.
fireTableDataChanged();
return true;
// If fail return false;
}
If delete is the action then first get the selected row and then call removeSelectedRow(int) like the following,
private void deleteRow() {
int selectedRow = table.getSelectedRow();
boolean deleteStatus = removeSelectedRow(selectedRow);
// Only if the deletion is success then delete from the file.
if(deleteStatus) {
// Delete it from the file too.
}
}
first you have to make sure that something has been selected: when there is something selected than enable the delete button. please look up the JTable java source code #
http://developer.classpath.org/doc/javax/swing/JTable-source.html
and the following code:
1331: /**
1332: * Receives notification when the row selection changes and fires
1333: * appropriate property change events.
1334: *
1335: * #param event the list selection event
1336: */
1337: public void valueChanged(ListSelectionEvent event)
1338: {
1339: firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
1340: Boolean.FALSE, Boolean.TRUE);
1341: int r = getSelectedRow();
1342: int c = getSelectedColumn();
1343: if (r != lastSelectedRow || c != lastSelectedColumn)
1344: {
1345: Accessible o = getAccessibleAt(lastSelectedRow,lastSelectedColumn);
1347: Accessible n = getAccessibleAt(r, c);
1348: firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n);
1350: lastSelectedRow = r;
1351: lastSelectedColumn = c;
1352: }
1353: }
You need to register for the last event to be notified when the selected rows have been changed. Add your own listener to enable the deletebutton based on whether or not a row has been selected which is as you can see in the event itself.
Please use to start with the DefaultTableModel because it will work in 90% of the cases.
And any change is applied to the tabledatamodel which will automatically propogate to the JTable View: normally you never change the view because all selection and scroll information is lost which is something you don't want.
When the delete button is fired the approach is straight forward: there is a row selected, otherwise it is impossible to click it: remove that selected row number from the defaultTableModel, and last but not least I would write simply the entire contents of the datamodel model to the designated file because the table's model hold the actual rows that are indeed displayed in the View.
So please think in terms of models models and models: Views are instantiated only once, packed scrolled etc and than you leave them as is. Models are normally also never changed: you change the contents of the models by adding and or deleting rows. One other tip: use always renderers: those that don't don't, in my humble opinion, don't understand how to work with JTables.
And yes you can leave out the first part to listen for selection changes: sure and pop up a warning to indicate the problem. And in a later stage add the functionality that listens for selection changes to enable and or disable the JButton delete row.
Hope this helps.

Categories