I have a JTable. User will enter an ID in a particular column and press TAB. I need to add event on that column to fetch value from DB and populate the rest of the columns of that row and create a new row for the next entry.
I am new to Swing and its difficult to find what is the best way to do it as i can see examples which were written in 2010 or so. Not sure if thats relevant still.
What I don't know:
adding event handler to a particular column's cell in table.
add next row after populating the data.
You can use a TableModelListener for this. When user change ID column value, tableChanged() is invoked. Then the relevant data is fetched from the DB and set in the row. And a new row is added as well. Try below example.
(For demonstration purpose I have used a mock database in this example. It only gives rows for IDs "111" and "222".)
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.DefaultTableModel;
import java.util.Vector;
public class TableDataChange
{
public static void main(String[] args)
{
DefaultTableModel tableModel = new DefaultTableModel(
new Object[][] {{"", "", ""}},
new Object[] {"ID", "Column 2", "Column 3"});
tableModel.addTableModelListener(new TableModelListener()
{
#Override
public void tableChanged(TableModelEvent e)
{
String id = (String) tableModel.getValueAt(e.getFirstRow(), 0);
if (id != null)
{
Vector row = Database.loadRowForId(id);
tableModel.getDataVector().set(e.getFirstRow(), row);
tableModel.addRow(new Vector());
}
}
});
JTable table = new JTable(tableModel);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new JScrollPane(table));
f.setBounds(300, 200, 400, 300);
f.setVisible(true);
}
}
// Mock database
class Database
{
static Vector loadRowForId(String id)
{
Vector row = new Vector();
if (id.equals("111"))
{
row.add("111");
row.add("aaa");
row.add("bbb");
}
else if (id.equals("222"))
{
row.add("222");
row.add("ppp");
row.add("qqq");
}
return row;
}
}
You can try something like this,
//Add Key Listener
table.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_TAB) {
int selectedColumn = table.getSelectedColumn();
//Now you can search records related to ID and populate the table
}
}
});
Related
In a previous question (Convert modelRowIndex to viewRowIndex for sorted JTable) I indicated that I was trying to create a "simple" JTable that used a TableModel to tie an ArrayList to a JTable using a TableModel. My goal was - and still is - to retain all of java's built-in JTable functionality that allows cell editing, row sorting, and column rearranging. Thanks to your help, that functionality now works.
I'm now trying to add the ability to Insert and Delete table rows. The (updated) example I provide here works ... EXCEPT ... under a certain sequence of operations, an "IndexOutOfBoundsException" is thrown. Here's my code:
package tableexample;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
public final class TableExample extends JFrame {
List<REItem> REList;
JTable tblREList;
JButton btnAddInsertRE, btnDeleteRE;
JScrollPane spMain;
JFrame frame;
Container pane;
public TableExample() {
// create and populate the ArrayList
REList = new ArrayList<>();
REList.add(new REItem("Template1", "Comment1"));
REList.add(new REItem("Template2", "Comment2"));
RETableModel retm = new RETableModel(REList); // Connect the List to the TableModel
// create GUI components
frame = new JFrame ("Table Example");
btnAddInsertRE = new JButton("Add/Insert");
btnDeleteRE = new JButton("Delete");
tblREList = new JTable(retm);
tblREList.setAutoCreateRowSorter(true);
spMain = new JScrollPane(tblREList);
// add button ActionListeners
btnAddInsertRE.addActionListener((ActionEvent evt) -> { btnAddInsertREActionPerformed(evt); });
btnDeleteRE.addActionListener((ActionEvent evt) -> { btnDeleteREActionPerformed(evt); });
// place GUI components and make the GUI visible
pane = frame.getContentPane();
pane.setLayout (null);
pane.add(btnAddInsertRE);
pane.add(btnDeleteRE);
pane.add(spMain);
btnAddInsertRE.setBounds (10, 10, 100, 25);
btnDeleteRE.setBounds (120, 10, 100, 25);
spMain.setBounds (10, 45, spMain.getPreferredSize().width, spMain.getPreferredSize().height);
frame.setSize(spMain.getWidth() + 40, spMain.getHeight() + 95);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
} // end TableExample constructor
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableExample notUsed = new TableExample();
});
} //end main
private void btnAddInsertREActionPerformed(ActionEvent evt) {
// Add a FileSelection object to the ArrayList
int r = tblREList.getSelectedRow(); // get row selection, if any
if (r < 0) { // no row selected
REList.add(new REItem("NewTemplate", "NewComment")); // append new item to end
r = REList.size()-1; // get index to new item
} else { // else no row selected
REList.add(r, new REItem("NewTemplate", "NewComment")); // insert above selected row
} // row selected or not
spMain.setViewportView(tblREList); // repaint the updated table
tblREList.getSelectionModel().setSelectionInterval(r, r); // select the new row
}
private void btnDeleteREActionPerformed(ActionEvent evt) {
int[] selRows = tblREList.getSelectedRows(); // see if any rows are selected
if (selRows.length>0) { // at least one row is selected
for (int r=selRows.length-1; r>=0; r--) { // delete each row, from the bottom up,
REList.remove(r); // so that indexes are correct and
} // don't change with each delete
tblREList.clearSelection(); // clear the row selection data
spMain.setViewportView(tblREList); // repaint the updated table
} else { // else no row(s) selected
JOptionPane.showMessageDialog(null, "Must select at least one item to delete");
} // no row selected
} // end btnDeleteREActionPerformed
public final class REItem {
String template;
String comment;
public REItem(String tmp, String cmt) {
this.template = tmp;
this.comment = cmt;
}
} // end class REItem
public class RETableModel extends AbstractTableModel {
private List<REItem> reList = new ArrayList();
private final String[] columnNames = { "Template", "Comment" };
public RETableModel(List<REItem> list){
this.reList = list;
}
#Override
public String getColumnName(int column){
return columnNames[column];
}
#Override
public int getRowCount() {
return reList.size();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public Object getValueAt(int row, int column) {
switch (column) {
case 0: return reList.get(row).template;
case 1: return reList.get(row).comment;
}
return null; // default case
}
#Override
public boolean isCellEditable(int row, int column) {
return true;
}
#Override
public Class<?> getColumnClass(int column){
switch (column){
case 0: return String.class;
case 1: return String.class;
}
return null; // default case
}
#Override
public void setValueAt(Object value, int row, int column) {
switch (column) {
case 0: reList.get(row).template = value.toString(); break;
case 1: reList.get(row).comment = value.toString(); break;
}
// uncommenting the below often causes IndexOutOfBoundsException: Invalid range exception
fireTableCellUpdated(row, column);
} // end setValueAt
} // end RETableModel
} // end class TableExample
The problem can be reproduced as follows: Run the above example, click the "Add/Insert" button to append a new row to the table, click either column header to re-sort the table, then click the "Delete" button: An "IndexOutOfBoundsException" is thrown, indicating that the "row" index specified by the TableModel's getValueAt method is flawed.
I assume the issue is related to the need for my TableModel.getValueAt (and maybe .setValueAt ???) method(s) to convert between TableModel column indexes and View column indexes but, for the life of me, I can't figure out how or where to make the conversion. Morevover, this question (Convert modelRowIndex to viewRowIndex for sorted JTable) indicates that a conversion between TableModel and View row-indexes is needed, AND that table re-sorting MUST occur before the index conversion is done.
Try as I might, I cannot figure out how to make the conversion and/or how to make sure that conversion happens AFTER the table is updated and re-sorted. Do I need a Listener? If so, what should it look like?
Can you provide some clarification and help?
First of all, variable names should NOT start with an upper case character. This is a Java convention and it messes with the formatting of the code you post making your code hard to read. Fix your variables and follow Java conventions.
The ArrayList should only be used to initially add data to the model.
After that updates should be done to the TableModel, not the ArrayList. So you need to add methods to your TableModel like addREItem(...) and removeREItem(...).
See Row Table Model for a step-by-step example of how to build a custom TableModel for a given object, including how the code the add???(...) and remove???(...) method.
And if you want to remove selected rows from the table then check out: How to delete multiple rows from JTable , database at a time for a working examples that shows how this can be done using the remove???(...) method.
I want to update a swing table's row value in the DB, clear the model, and load the new model with the new updated value. The problem is that when I click the button to update the value, the model is cleared but no data is shown in the table. What am I doing wrong?
Parts of the code not relative to the table have been removed in order to make the example easier.
public class ShowValues extends JFrame {
//Attributes
private DefaultTableModel model;
private JTable table = new JTable()
{
public boolean isCellEditable(int row, int column)
{
return false;
};
};
public ShowValues() {
btnDeleteItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0)
{
int selectedRowNumber = table.getSelectedRow();
String selectedItemId = (String) table.getModel().getValueAt(selectedRowNumber, 0);
//code to change selected item's state to deleted in the DB
model.setRowCount(0); //clear model
showTable(); //load new model
labelMsg.setText("Item deleted");
}
});
btnDeleteItem.setBounds(200, 320, 203, 57);
contentPane.add(btnDeleteItem);
}
public void showTable()
{
String columnNames[] = {"Item Id", "Item Name", "Item price"};
//code to get the data form the DB
String dataValues[][] = new String[itemNum][3];
for(int i=0; i<itemNum; i++)
{
dataValues[i][0] = idItem[i];
dataValues[i][1] = nameItem[i];
dataValues[i][2] = priceItem[i];
}
model = new DefaultTableModel(dataValues, columnNames);
table.setModel(model);
scrollPane = new JScrollPane(table);
scrollPane.setBounds(17, 20, 490, 205);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
topPanel.add(scrollPane);
}
}
//code to change selected item's state to deleted in the DB
Why would you want to reload all the data just because the change the value of one property in the row? That is not very efficient.
Just use the setValueAt(...) method to change the value in the JTable.
The problem is that when I click the button to update the value, the model is cleared but no data is shown in the table
The problem is you create a new JTable, but you never add the table to the viewport of your JScrollPane.
The solution is to NOT create a new JTable. Just create a new TableModel. Then you can just use:
table.setModel( theNewlyCreateTableModel );
I want show customer details from a MySQL database in a JTable, but I don't see any result in my panel when I click the "show" button.
This is the method to add the table to the JScrollPane:
void addTable()
{
for(int i=0 ; i<myTableModel.getColumnCount();i++)
{
myTableModel.getColumnName(i);
}
showCustomers();
table.setModel(myTableModel);//mytablemodel is a object from MyTableModel Class
scrollPane.add(table); //was on another part of program but i edit it for helping to answers
panel_show.add(scrollPane);
}
And here is MyTableClass, implementing TableModel, with a new method, addCustomer. addCustomer will add a Customer to the CustomerList. I am trying ArrayList for first time to show Customer data but it doesn't work.
Also, columnName will make the table headers with the column names from the database.
public class MyTableModel implements TableModel
{
private ArrayList<Customer> customerList;
private String[] columnName =
{"id", "name", "family", "idc", "age", "sex", "balance", "tel", "haveFamily", "population"};
#Override
public int getRowCount()
{
//return customerList.size();
return 1;
}
#Override
public int getColumnCount()
{
return 10;
}
#Override
public String getColumnName(int columnIndex)
{
return columnName[columnIndex];
}
#Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == 0)
return Integer.class;
return String.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(columnIndex == 0)
return customerList.get(rowIndex).getId();
else if(columnIndex == 1)
return customerList.get(rowIndex).getName();
else if(columnIndex == 2)
return customerList.get(rowIndex).getFamily();
else if(columnIndex == 3)
return customerList.get(rowIndex).getIdc();
else if(columnIndex == 4)
return customerList.get(rowIndex).getDate();
else if(columnIndex == 5)
return customerList.get(rowIndex).getSex();
else if(columnIndex == 6)
return customerList.get(rowIndex).getBalance();
else if(columnIndex == 7)
return customerList.get(rowIndex).getTel();
else if(columnIndex == 8)
return customerList.get(rowIndex).isHaveFamily();
else if(columnIndex == 9)
return customerList.get(rowIndex).getPopulation();
else
return null;
}
public void addCustomer(Customer customer)
{
CustomerManager customerManager = new CustomerManager();
customerManager.addCustomer(customer);
customerList.add(customer);
}
The showCustomer method will be open a database connection and create a TableModel with customers, which is then used for the table on my panel.
public void showCustomers()
{
Connection conn = null;
try
{
//String user = "root";
// String pass = null;
//String url = "jdbc:mysql://localhost/estate";
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/estate", "root", null);
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * from customer");
//int columnCount = rsmd.getColumnCount();
while (rs.next())
{
myTableModel.addCustomer(new Customer(1,"david","schmidt","0025674433","31","Male","200000","4545552132","true","2");
}//Original Form --> Customer({"id","name","family","idc","age",
// "sex","balance","tel","haveFamily","population"});
}
In MyTableModel I make an addCustomer method and fill the customer data using a ResultSet. Is it needed?
My question: How can I show my Customers using a JTable?
After some fiddling I now also have a problem with myTableModel.addCustomer(rs.getInt(), ...). The error I am getting is this:
Error in addCustomer,Duplicate entry '0' for KEY PRIMARY
However, I don't have a customer with an ID of 0.
When displaying a JTable you first add the table to a scroll pane and then you add the scroll pane to a panel.
When adding a component to a visible GUI you need to make sure the layout manager has been invoked so that the component has a size and location.
So the basic code would be:
table.setModel(...);
JScrollPane scrollPane = new JScrollPane( table );
panel.add( scrollPane );
panel.revalidate();
panel.repaint();
The other approach is the create an empty table and add the scroll pane to the GUI when you first create the GUI. Then all the code you need is:
table.setModel(...);
Edit:
You question is about displaying a table in a panel, when you click a button. So that is ALL the SSCCE should do. Where the data comes from is irrelevant, so posting code dealing with a database is completely unnecessary, because we can't execute the code. A custom TableModel is irrelevant to the real question.
Here is a simple SSCCE that uses my second suggestion of updating an existing table with a new model. Every time you click the button the number of columns changes. This simulates getting new data from somewhere.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class RefreshSSCCE extends JPanel
{
private JTable table = new JTable();
private int columns = 3;
public RefreshSSCCE()
{
setLayout( new BorderLayout() );
JButton refresh = new JButton( "Refresh Data" );
add(refresh, BorderLayout.NORTH);
refresh.addActionListener( new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
refreshData();
}
});
add(new JScrollPane(table), BorderLayout.CENTER);
}
private void refreshData()
{
DefaultTableModel model = new DefaultTableModel(5, columns++);
table.setModel( model );
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Refresh SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( new RefreshSSCCE() );
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
See how easy that code is to understand? The code is complete and in one class. It can easily be copied and pasted so others can test the code.
When you simplify the problem then solution is usually much easier. That is why you take the time to create a SSCCE. Even if the SSCCE doesn't work the way you want, we only have a couple of lines of code to look at to understand what you are attempting to do. There is no need to complicate the question with SQL code.
Once you get the simple code working you then modify the refreshData method to get real data. That is what I mean by hardcoding data. There is no need for a dynamic query of the database to demonstrate your problem of displaying a table in a panel.
I want to post a picture, but I don't have reputation on this site, but I described my problem below :
| Name | Grade |
__________________
| Febri | 60| <---- if this is a cell (row0,column1),
I can't retrieve data on this cell if cursor is
still pointing inside that cell.
take a look :
System.out.println(mytable.getValueAt(0,0)); --> output : Febri
System.out.println(mytable.getValueAt(0,1)); -- > output :
that's because my mouse's cursor is still pointing inside that cell ..
Any suggestions? Is this problem related to mouse listener?
Please help , thx.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class myTable extends JFrame {
String heads[] = { "Name", "Grade" };
Object[][] data = { { "Febri", "60" } };
JTable table;
JButton button;
JScrollPane scroll;
public myTable() {
setLayout(new FlowLayout());
table = new JTable(data, heads);
scroll =new JScrollPane(table);
button = new JButton("Retrieve Data");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
System.out.println(table.getValueAt(0, 0));
System.out.println(table.getValueAt(0, 1));
}
}
});
add(scroll);
}
public static void main(String args[])
{
myTable tbl = new myTable();
tbl.setVisible(true);
tbl.setSize(500, 400);
} }
Your example works (mostly) fine.
I assume you mean while the cell is editable it won't return the value which the editor is showing?
This makes sense, as the value contained by the editor hasn't yet been committed to the model.
What you can do is stop the current editing process if the table is in edit model, this will commit the value from the editor to the model, which you can then read...
public void actionPerformed(ActionEvent e) {
if (e.getSource() == button) {
// Is the table in edit mode?
if (table.isEditing()) {
// Get the current editor
TableCellEditor editor = table.getCellEditor();
if (editor != null) {
// Try and stop the cell editing process
if (!editor.stopCellEditing()) {
// Cancel the editing if can't be stopped...
// You could handle an error state here instead...
editor.cancelCellEditing();
}
}
}
System.out.println(table.getValueAt(0, 0));
System.out.println(table.getValueAt(0, 1));
}
}
Of course, this all comes down to what it is you are trying to achieve...
You need to stop editing of the cell. Table Stop Editing shows two ways to do this. One as demonstrated by MadProgrammer and the other allows you so set a property on the JTable.
Can someone help me with this listener?
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e){
if(e.getValueIsAdjusting()){
ListSelectionModel model = table.getSelectionModel();
int lead = model.getLeadSelectionIndex();
displayRowValues(lead);
}
}
private void displayRowValues(int rowIndex){
String country = "";
Object oCountry = table.getValueAt(rowIndex, 0);
country += oCountry.toString();
countryTxt.setText(country );
}
});
It's supposed to send data from cell in jtable (table) into a textfield (countryTxt) when one of the row's is selected, but it works only when I click on row and not when I'm cycling trough my table with arrow key's.
The problem is with this line:
if (e.getValueIsAdjusting()) {
Replace this with:
if (e.getValueIsAdjusting()) return;
This is a check for multiple selection events BTW.
If you comment out e.getValueIsAdjusting() it works.
http://docs.oracle.com/javase/6/docs/api/javax/swing/ListSelectionModel.html#setValueIsAdjusting(boolean)