My problem is:
I have a button that will add components to a JList when it is clicked. Each row of the list is composed by two jtextFields. The first is the ID of the row. Every time I press the "Add button", a new row appears, the ID is incremented, and the person who is using the application will write whatever he want on the second jTextField (forward the ID field).
Then it will have a scroll pane for when there are more than 4 rows.
And I want a Remove Button too. To be possible to remove some rows.
Can you help me with this? I don't know how to create a list like this...
Thanks!
Simply use JTable with two columns. This will allow you to establish two columns, one for the ID and one for the value. This will allow you make the second column editable.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class TestTable04 {
public static void main(String[] args) {
new TestTable04();
}
private int id = 0;
public TestTable04() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
final RecordTableModel model = new RecordTableModel();
JTable table = new JTable(model);
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.add(new Record(++id));
}
});
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(add, BorderLayout.SOUTH);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Record {
private int id;
private String value;
public Record(int id) {
this.id = id;
}
public int getID() {
return id;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public class RecordTableModel extends AbstractTableModel {
private List<Record> lstRecords;
public RecordTableModel() {
lstRecords = new ArrayList<>(24);
}
public void add(Record record) {
lstRecords.add(record);
fireTableRowsInserted(lstRecords.size() - 1, lstRecords.size() - 1);
}
public void remove(Record record) {
if (lstRecords.contains(record)) {
int index = lstRecords.indexOf(record);
remove(index);
}
}
public void remove(int index) {
lstRecords.remove(index);
fireTableRowsDeleted(index, index);
}
#Override
public int getRowCount() {
return lstRecords.size();
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
Class clazz = String.class;
switch (columnIndex) {
case 0:
clazz = Integer.class;
break;
}
return clazz;
}
#Override
public String getColumnName(int column) {
String name = null;
switch (column) {
case 0:
name = "ID";
break;
case 1:
name = "Value";
break;
}
return name;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Record record = lstRecords.get(rowIndex);
Object value = null;
switch (columnIndex) {
case 0:
value = record.getID();
break;
case 1:
value = record.getValue();
break;
}
return value;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 1;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
Record record = lstRecords.get(rowIndex);
switch (columnIndex) {
case 1:
record.setValue(aValue == null ? null : aValue.toString());
fireTableCellUpdated(rowIndex, columnIndex);
break;
}
}
}
}
Check out How to use tables for more details
You can try creating a custom ListCellRenderer and use JList's setRenderer method to assign it. Override the getListCellRenderer() method of ListCellRenderer to return a JPanel containing two components, a JLabel for the id, and a JTextfield for the user input. You may need to keep a reference to the JTextField elsewhere so that you can retrieve the user input.
To implement the add and remove methods, I would recommend backing the JList with a ListModel. Add and remove to the ListModel will also update the JList UI.
Related
I have a JTable where one of the cell is a LocalDate which I can update by clicking on it and it opens a date picker. The problem is that the modification is not taken into account because the date picker event waits for an input from the user but does not block. And so the event that handles the edit ends before it receives the edit.
I've checked this issue : How to wait for an event to complete, before the program continue running in java and tried to block event child with CountDownLatch but it freezes the app. I'm not sure but I guess it is something about event blocking within another event that causes trouble.
Here's some code to test :
For editing the cell :
package com.swing.datepicker;
import com.github.lgooddatepicker.components.DatePicker;
import javax.swing.*;
import javax.swing.table.TableCellEditor;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.LocalDate;
public class DateOfBirthCellEditor extends AbstractCellEditor implements TableCellEditor, ActionListener {
private LocalDate curValue;
private final JButton button;
public DateOfBirthCellEditor() {
button = new JButton();
button.addActionListener(this);
button.setBorderPainted(false);
}
#Override
public void actionPerformed(ActionEvent e) {
DatePicker datePicker = new DatePicker();
datePicker.setDate(curValue);
button.add(datePicker);
datePicker.openPopup();
datePicker.addDateChangeListener(dateChangeEvent -> {
curValue = dateChangeEvent.getNewDate();
System.out.println("should happens before - curValue : " + curValue);
});
System.out.println("should happens after - curValue : " + curValue);
button.remove(datePicker);
fireEditingStopped();
}
#Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
curValue = (LocalDate) value;
return button;
}
#Override
public Object getCellEditorValue() {
return curValue;
}
}
main class :
package com.swing.datepicker;
import javax.swing.*;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import java.time.LocalDate;
import java.util.List;
public class TableTest extends JFrame {
public TableTest() throws Exception {
super("TEST");
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
initUI();
}
static class Dude {
String name;
LocalDate dateofbirth;
public Dude(String name, LocalDate dateofbirth) {
this.name = name;
this.dateofbirth = dateofbirth;
}
}
public void initUI() {
// Initial Settings
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(600, 400);
this.setLocationRelativeTo(null);
JPanel contentPane = (JPanel) this.getContentPane();
TableModel tableModel = new TableModel() {
final List<Dude> data = List.of(new Dude("John", LocalDate.of(1998, 6,30)));
final String[] headers = {"name", "date of birth"};
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return headers.length;
}
#Override
public String getColumnName(int columnIndex) {
return headers[columnIndex];
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 1) {
return LocalDate.class;
}
return Object.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return switch (columnIndex) {
case 0 -> data.get(rowIndex).name;
case 1 -> data.get(rowIndex).dateofbirth;
default -> null;
};
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (aValue != null) {
switch (columnIndex) {
case 0 -> data.get(rowIndex).name = (String) aValue;
case 1 -> data.get(rowIndex).dateofbirth = (LocalDate) aValue;
}
}
}
#Override
public void addTableModelListener(TableModelListener l) {}
#Override
public void removeTableModelListener(TableModelListener l) {}
};
JTable table = new JTable(tableModel);
table.setDefaultEditor(LocalDate.class, new DateOfBirthCellEditor());
JPanel tablePanel = new JPanel();
tablePanel.add(table);
contentPane.add(tablePanel);
}
public static void main(String[] args) throws Exception {
TableTest window = new TableTest();
window.setVisible(true);
}
}
I used that date picker in other parts of my code, that's why I wanna keep it and do some weird code in editing the cell. I have this in my pom :
<dependency>
<groupId>com.github.lgooddatepicker</groupId>
<artifactId>LGoodDatePicker</artifactId>
<version>11.2.1</version>
</dependency>
Thanks in advance.
I have a JTable with some rows. How can i block edit row just when radiobutton unlock is selected? Below a small project to use as a working example
Update
ElencoPersoneFrame class
package test;
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class ElencoPersoneFrame extends JFrame {
private PersonaTableModel tableModel;
private JTable table;
private JScrollPane scrollPane;
JRadioButton rdbtnFilm = new JRadioButton("Editable");
JRadioButton rdbtnSerieTv = new JRadioButton("Not editable");
public ElencoPersoneFrame()
{
super ("Elenco Persone");
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setSize(400, 250);
ArrayList<Persona> listaPersone = new ArrayList<Persona>();
listaPersone.add(new Persona("Mario", "Rossi", 1972, false));
listaPersone.add(new Persona("Giacomo", "Bianchi", 1946, false));
listaPersone.add(new Persona("Roberto", "Verdi", 1985, true));
tableModel = new PersonaTableModel(listaPersone);
table = new JTable(tableModel);
scrollPane = new JScrollPane(table);
JPanel rdpnl=new radioPanel();
getContentPane().add(rdpnl, BorderLayout.NORTH);
getContentPane().add(scrollPane, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ElencoPersoneFrame f = new ElencoPersoneFrame();
f.setVisible(true);
}
});
}
}
Persona class
package test;
public class Persona
{
private String nome;
private String cognome;
private int annoNascita;
private boolean disoccupato;
/*costruttore*/
public Persona(String nome, String cognome, int annoNascita, boolean disoccupato) {
this.nome = nome;
this.cognome = cognome;
this.annoNascita = annoNascita;
this.disoccupato = disoccupato;
}
public String getNome() { return nome; }
public String getCognome() { return cognome; }
public int getAnnoNascita() { return annoNascita; }
public boolean isDisoccupato() { return disoccupato; }
public void setNome(String nome) { this.nome = nome; }
public void setCognome(String cognome) { this.cognome = cognome; }
public void setAnnoNascita(int annoNascita) { this.annoNascita = annoNascita; }
public void setDisoccupato(boolean disoccupato) { this.disoccupato = disoccupato; }
}
PersonaTableModel class
package test;
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
public class PersonaTableModel extends AbstractTableModel
{
private ArrayList<Persona> listaPersone;
public PersonaTableModel(ArrayList<Persona> listaPersone) {
this.listaPersone = listaPersone;
}
public int getRowCount() {
return listaPersone.size();
}
public int getColumnCount() {
return 4;
}
public String getColumnName(int column) {
switch (column) {
case 0: return "Nome";
case 1: return "Cognome";
case 2: return "Anno nascita";
case 3: return "Disoccupato?";
}
return "";
}
public Class getColumnClass(int column) {
switch (column) {
case 0: return String.class;
case 1: return String.class;
case 2: return Number.class;
case 3: return Boolean.class;
}
return Object.class;
}
public boolean isCellEditable(int row, int column) {
return true;
}
public Object getValueAt(int row, int column) {
Persona p = listaPersone.get(row);
switch (column) {
case 0: return p.getNome();
case 1: return p.getCognome();
case 2: return p.getAnnoNascita();
case 3: return p.isDisoccupato();
}
return null;
}
public void setValueAt(Object value, int row, int column)
{
Persona p = listaPersone.get(row);
switch (column)
{
case 0: p.setNome((String) value); break;
case 1: p.setCognome((String) value); break;
case 2: p.setAnnoNascita((Integer) value); break;
case 3: p.setDisoccupato((Boolean) value); break;
}
}
public void aggiungiPersona(Persona p) {
listaPersone.add(p);
int row = listaPersone.size() - 1;
fireTableRowsInserted(row, row);
}
}
radioPanel class
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class radioPanel extends JPanel implements ActionListener
{
private JRadioButton unlock;
private JRadioButton lock;
private ButtonGroup rdgroup;
public radioPanel()
{
rdgroup=new ButtonGroup();
unlock = new JRadioButton("Editable");
lock = new JRadioButton("Not editable");
rdgroup.add(unlock);
rdgroup.add(lock);
rdgroup.setSelected(unlock.getModel(), true);
this.add(unlock);
this.add(lock);
lock.addActionListener(this);
unlock.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource() == this.lock)
{
}
if(e.getSource() == this.unlock)
{
}
}
}
So there doesn't seem to be (AFAIK) some magic method to set the table uneditable. What you can do though is create a method in your model, like setEditable(boolean), where you can set a class member boolean editable. Use the same field for isCellEditable. After you change the state, you should fire the table change. Something like
class PersonaTableModel extends AbstractTableModel {
...
private boolean editable = true;
public boolean isEditable() {
return editable;
}
public boolean isCellEditable(int row, int column) {
return editable;
}
public void setEditable(boolean editable) {
this.editable = editable;
fireTableDataChanged();
}
...
}
You'll also need to way for the ActionListener to get hold of the table model. So you can refactor your radio panel to something like
class radioPanel extends JPanel {
private JRadioButton unlock;
private JRadioButton lock;
private ButtonGroup rdgroup;
...
public ButtonGroup getButtonGroup() {
return rdgroup;
}
public AbstractButton getUnlock() {
return unlock;
}
public AbstractButton getLock() {
return lock;
}
}
This way you can just get the radio buttons from anywhere and add the ActionListener. So you can change you main class code to something like:
radioPanel rdpnl = new radioPanel();
rdpnl.getUnlock().addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
tableModel.setEditable(true);
}
});
rdpnl.getLock().addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
tableModel.setEditable(false);
}
});
getContentPane().add(rdpnl, BorderLayout.NORTH);
Tested, and seems to work as expected.
Aside: Please make note of Java naming convention. Class names should begin with upper case letters i.e. radioPanel → RadioPanel
You need to implement the isCellEditable method for your TableModel:
public boolean isCellEditable(int row, int col)
return false;
See also the tutorials.
I am trying to delete multiple rows (per example, five of fifty) in a Jtable, but I can only delete one at time (I am using multiple interval selection before you ask!), and I feel the Jtable freeze a bit. My delete button:
deleteButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e){
SwingUtilities.invokeLater(new Runnable() {
public void run(){
int[] selectedRow = jTable.getSelectedRows();
for(int j=0; j<selectedRow.length; j++){
Boolean state= (Boolean)jTable.getValueAt(selectedRow[j],10);
if(state==true){//deleta the row
User u=model.getUsers(selectedRow[j]);
new UserDao().delete(u);
model.remove(selectedRow[j]);
numberField.setText(String.valueOf(model.getRowCount()));
}
}
}
});
}
});
My remove:
public void remove(int row) {
this.userlist.remove(row);
this.fireTableDataChanged();
}
What I am doing wrong?
Let's take a closer look at the code...
deleteButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e){
// Don't know why you need to use invokeLater
// The event should be trigged within the EDT if the user
// clicked the button. This may introduce a small "pause"
// depending on what else is in the EDT...
SwingUtilities.invokeLater(new Runnable() {
public void run(){
// Get the indices of the selected rows...okay
int[] selectedRow = jTable.getSelectedRows();
// Loop through the selected rows...good...
for(int j=0; j<selectedRow.length; j++){
// Get the "state" of the row...okay
Boolean state= (Boolean)jTable.getValueAt(selectedRow[j],10);
// Long winded if, but okay...
if(state==true){//deleta the row
// Don't know what's going on here,
// But I assume you are trying to delete
// something from some kind of database
// THIS is likely to chew up some time...
User u=model.getUsers(selectedRow[j]);
new UserDao().delete(u);
// Uh oh...
// If you remove a row from the model, the list of indices you
// have is now invalid, as they no longer point
// to the correct rows in the model
model.remove(selectedRow[j]);
numberField.setText(String.valueOf(model.getRowCount()));
}
}
}
});
}
});
So. Two problems.
You seem to be calling some kind of management functionality within the context of the EDT, functionality that "looks" like it's going to cause the EDT to be slowed/paused for even a small amount of time...
You are relying on out of date information...
A better solution would be to use some kind of background process to perform the deleting of the User and provide a means within the model to look up the User object itself and remove it from the model. This removes the possibility of the indices being changed under you.
A SwingWorker provides a means by which we can perform operations in the background, off the Event Dispatching Thread, while provide means to re-sync required actions (like modifying the table model) back onto the EDT when required...
For example...
public class DeleteUsersWorker extends SwingWorker<List<User>, User> {
private UserTableModel model;
private List<User> users;
public DeleteUsersWorker(UserTableModel model, List<User> users) {
this.model = model;
this.users = users;
}
protected List<User> doInBackground() {
UserDao dao = new UserDao();
for (User user : users) {
dao.delete(user);
publish(user);
}
return users;
}
protected void process(List<User> users) {
for (User user : users) {
model.remove(user);
}
}
}
And the contents of the actionPerformed method...
int[] selectedRow = jTable.getSelectedRows();
List<User> usersToBeRemoved = new ArrayList<>(selectedRow.length);
for(int row : selectedRow){
// Is state part of the User object??
Boolean state = (Boolean)jTable.getValueAt(row,10);
if(state){
usersToBeRemoved.add(model.getUsers(row));
}
}
DeleteUsersWorker worker = new DeleteUsersWorker(model, users);
worker.execute();
This will probably require to add some additional functionality to the table model to support removing the User object from the model, but I don't have your model, so it's difficult to make suggestions...
Take a look at Concurrency in Swing for more details...
A better solution might be to have a listener on your dao API that could provide notifications about updates, this way the model could update itself, but again, not enough context to make a determination ;)
Updated with comments form TrashGod
You should also beware that the view indices don't always map directly to the model indices. This happens when the table is sorted or filtered. While you might argue that your table isn't (sorted or filtered) it is good practice never to make such assumptions...
When taking a row index from the table, you should call JTable#convertRowIndexToModel(int) which will return you the index point in the model
Take a look at Sorting and Filtering for more details...
Update with runnable example
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class TableDeleteRowsTest {
public static void main(String[] args) {
new TableDeleteRowsTest();
}
public TableDeleteRowsTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final UserTableModel model = new UserTableModel(
new User("Kermit"),
new User("Fozzie"),
new User("Animal"),
new User("Miss Piggy"),
new User("Gonzo"),
new User("Beaker"),
new User("Crazy Harry"),
new User("Floyd Pepper"),
new User("Sweetums"));
final JTable table = new JTable(model);
JButton delete = new JButton("Delete");
delete.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int[] selectedRows = table.getSelectedRows();
if (selectedRows.length > 0) {
List<User> users = new ArrayList<>(selectedRows.length);
for (int row : selectedRows) {
int modelRow = table.convertRowIndexToModel(row);
Boolean selected = (Boolean) model.getValueAt(modelRow, 1);
if (selected) {
users.add(model.getUser(modelRow));
}
}
if (users.size() > 0) {
new DeleteUserWorker(users, model).execute();
}
}
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.add(delete, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DeleteUserWorker extends SwingWorker<List<User>, User> {
private List<User> users;
private UserTableModel model;
public DeleteUserWorker(List<User> users, UserTableModel model) {
this.users = users;
this.model = model;
}
#Override
protected void process(List<User> chunks) {
for (User user : users) {
model.remove(user);
}
}
#Override
protected List<User> doInBackground() throws Exception {
for (User user : users) {
// Simulated delay
Thread.sleep(250);
publish(user);
}
return users;
}
}
public class UserTableModel extends AbstractTableModel {
private List<User> users;
private List<Boolean> selected;
public UserTableModel(User... users) {
this.users = new ArrayList<>(Arrays.asList(users));
selected = new ArrayList<>(this.users.size());
for (User user : this.users) {
selected.add(new Boolean(false));
}
}
public User getUser(int row) {
return users.get(row);
}
#Override
public int getRowCount() {
return users.size();
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public String getColumnName(int column) {
String name = "?";
switch (column) {
case 0:
name = "User";
break;
case 1:
name = "";
break;
}
return name;
}
#Override
public Class getColumnClass(int column) {
Class type = String.class;
switch (column) {
case 0:
type = String.class;
break;
case 1:
type = Boolean.class;
break;
}
return type;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Object value = null;
switch (columnIndex) {
case 0:
value = users.get(rowIndex).getName();
break;
case 1:
value = selected.get(rowIndex);
break;
}
return value;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 1;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
switch (columnIndex) {
case 1:
if (aValue instanceof Boolean) {
selected.set(rowIndex, (Boolean) aValue);
fireTableCellUpdated(rowIndex, columnIndex);
}
break;
}
}
public void remove(User user) {
int index = users.indexOf(user);
if (index >= 0) {
selected.remove(index);
users.remove(user);
fireTableRowsDeleted(index, index);
}
}
}
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}
Updated with additional example
The above example will only delete the rows that are marked AND selected. To delete all the marked rows, you will need something more like...
JButton delete = new JButton("Delete");
delete.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
List<User> users = new ArrayList<>(selectedRows.length);
for (int row = 0; row < table.getRowCount(); row++) {
int modelRow = table.convertRowIndexToModel(row);
Boolean selected = (Boolean) model.getValueAt(modelRow, 1);
if (selected) {
users.add(model.getUser(modelRow));
}
}
if (users.size() > 0) {
new DeleteUserWorker(users, model).execute();
}
}
});
Hi,
I have created my TableModel and want to refresh JTable once I added a new row. What should be added to the listener to "refresh" JTable?
public class MyTableModel implements TableModel {
private Set<TableModelListener> listeners = new HashSet<TableModelListener>();
//List<Staff> staffs = Factory.getInstance().getStaffDAO().getAllStaff();
private List<Staff> staffs;
public MyTableModel(List<Staff> staffs){
this.staffs = staffs;
}
#Override
public int getRowCount() {
return staffs.size();
}
#Override
public int getColumnCount() {
return 5;
}
#Override
public String getColumnName(int columnIndex) {
switch (columnIndex){
case 0:
return "First Name";
case 1:
return "Second Name";
case 2:
return "Date";
case 3:
return "Position";
case 4:
return "Salary";
}
return "";
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return Object.class;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Staff staff = staffs.get(rowIndex);
switch (columnIndex){
case 0:
return staff.getName();
case 1:
return staff.getSurname();
case 2:
return staff.getDate();
case 3:
return staff.getPosition();
case 4:
return staff.getSalary();
}
return "";
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
#Override
public void addTableModelListener(TableModelListener l) {
}
#Override
public void removeTableModelListener(TableModelListener l) {
}
}
Here is my listener of my Add row Button:
#Override
public void actionPerformed(ActionEvent e) {
Staff staff = new Staff();
staff.setName(JOptionPane.showInputDialog("Enter First Name"));
staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
staff.setDate(JOptionPane.showInputDialog("Enter Date"));
staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
try {
Factory.getInstance().getStaffDAO().addStaff(staff);
} catch (SQLException e1) {
e1.printStackTrace();
}
!!!Here should be some code that will be firing my table after adding new row!!!
}
I've tried to use method firetabledatachanged() of AbstractTableModel in my actionPerformed() but with unluck, it is appeared ClassCastException.
UPDATE 1
WorkPlaceGui.java
public class WorkPlaceGui extends JFrame implements ActionListener {
AbstractTableModel model;
JTable jTable;
JScrollPane jScrollPane;
public WorkPlaceGui()throws SQLException{
List<Staff> staffs = Factory.getInstance().getStaffDAO().getAllStaff();
for(int i = 0; i < 0; i++) {
staffs.add(new Staff("First Name " + staffs.get(i).getName(), "Second Name " + staffs.get(i).getSurname(), "Date " + staffs.get(i).getDate(), "Position " + staffs.get(i).getPosition(), "Salary " + staffs.get(i).getSalary()));
}
model = new MyTableModel(staffs);
jTable = new JTable(model);
JButton jBtnAdd = new JButton("Добавить");
JButton jBtnDel = new JButton("Удалить");
JButton jBtnUpd = new JButton("Обновить");
JButton jBtnAdmin = new JButton("Админка");
JPanel panelNorth = new JPanel();
JPanel panelCenter = new JPanel();
JPanel panelSouth = new JPanel();
jTable.setPreferredScrollableViewportSize(new Dimension(350, 150));
jScrollPane = new JScrollPane(jTable);
panelNorth.setLayout(new FlowLayout());
panelNorth.add(jBtnAdd);
panelNorth.add(jBtnDel);
panelNorth.add(jBtnUpd);
panelNorth.add(jBtnAdmin);
panelCenter.add(jScrollPane);
setLayout(new BorderLayout());
add(panelNorth, BorderLayout.NORTH);
add(panelCenter, BorderLayout.CENTER);
jBtnAdd.addActionListener(this);
setPreferredSize(new Dimension(550, 300));
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setTitle("Staff data base");
pack();
setVisible(true);
setLocationRelativeTo(null);
}
#Override
public void actionPerformed(ActionEvent e) {
Staff staff = new Staff();
staff.setName(JOptionPane.showInputDialog("Enter First Name"));
staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
staff.setDate(JOptionPane.showInputDialog("Enter Date"));
staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
try {
Factory.getInstance().getStaffDAO().addStaff(staff);
} catch (SQLException e1) {
e1.printStackTrace();
}
model.fireTableDataChanged();
}
}
MyTableModel.java
public class MyTableModel extends AbstractTableModel {
private List<Staff> staffs;
public MyTableModel(List<Staff> staffs){
this.staffs = staffs;
}
#Override
public int getRowCount() {
return staffs.size();
}
#Override
public int getColumnCount() {
return 5;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Staff staff = staffs.get(rowIndex);
switch (columnIndex){
case 0:
return staff.getName();
case 1:
return staff.getSurname();
case 2:
return staff.getDate();
case 3:
return staff.getPosition();
case 4:
return staff.getSalary();
}
return "";
}
}
You've done it the hard way.
First of all, you've implemented directly from TableModel and secondly you've failed to implement the listener requirements...
Instead, try extending from the AbstractTableModel instead, which already includes the implementations of the listener registration and notification.
You will need to provide a method that will allow you to add a row to the table model. In this method you need to use the fireTableRowsInserted method which will notify any tables using the model, that a new row has been added...
Update with example
This is VERY, VERY basic example. It's only intention is to demonstrate the use of fireTableRowsInserted. It uses a Swing Timer to add a new row every 125 milliseconds until you kill it ;)
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class DynamicTable {
public static void main(String[] args) {
new DynamicTable();
}
public DynamicTable() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final MyTableModel model = new MyTableModel();
JTable table = new JTable(model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(125, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.addRow();
}
});
timer.start();
}
});
}
public class MyTableModel extends AbstractTableModel {
private List<String[]> rows;
public MyTableModel() {
rows = new ArrayList<>(25);
}
#Override
public int getRowCount() {
return rows.size();
}
#Override
public int getColumnCount() {
return 4;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
String[] row = rows.get(rowIndex);
return row[columnIndex];
}
public void addRow() {
int rowCount = getRowCount();
String[] row = new String[getColumnCount()];
for (int index = 0; index < getColumnCount(); index++) {
row[index] = rowCount + "x" + index;
}
rows.add(row);
fireTableRowsInserted(rowCount, rowCount);
}
}
}
Updated with another example
Because your table model is backed by its own List, it has no connection to your factory. It doesn't know when you add or remove objects from it. This means you become responsible for updating the model:
public class MyTableModel extends AbstractTableModel {
private List<Staff> staffs;
public MyTableModel(List<Staff> staffs){
this.staffs = staffs;
}
#Override
public int getRowCount() {
return staffs.size();
}
#Override
public int getColumnCount() {
return 5;
}
public void add(Staff staff) {
int size = getSize();
staffs.add(staff);
fireTableRowsInserted(size, size);
}
public void remove(Staff staff) {
if (staffs.contains(staff) {
int index = stafff.indexOf(staff);
staffs.remove(staff);
fireTableRowsDeleted(index, index);
}
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Staff staff = staffs.get(rowIndex);
switch (columnIndex){
case 0:
return staff.getName();
case 1:
return staff.getSurname();
case 2:
return staff.getDate();
case 3:
return staff.getPosition();
case 4:
return staff.getSalary();
}
return "";
}
}
And your actionPerformed:
#Override
public void actionPerformed(ActionEvent e) {
Staff staff = new Staff();
staff.setName(JOptionPane.showInputDialog("Enter First Name"));
staff.setSurname(JOptionPane.showInputDialog("Enter Second Name"));
staff.setDate(JOptionPane.showInputDialog("Enter Date"));
staff.setPosition(JOptionPane.showInputDialog("Enter Position"));
staff.setSalary(JOptionPane.showInputDialog("Enter Salary"));
try {
Factory.getInstance().getStaffDAO().addStaff(staff);
((MyTableModel)model).add(staff);
} catch (SQLException e1) {
e1.printStackTrace();
}
}
Your class MyTableModel implements TableModel, but it has no event handling mechanism to connect the model to the view. Instead extend AbstractTableModel, as shown here and here. AbstractTableModel provides the fireTable* methods needed for this.
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
Well, first of all, find more about Observer pattern (http://en.wikipedia.org/wiki/Observer_pattern).
I suggest that you create a ObservableModel class that will have a list of PropertyChangeListeners. Your StaffDAO should extend this ObservableModel. When the new staff is added (i.e. addStaff is called) you should call ObservableModel's firePorpertyChange or something like that. firePropertyChange notifyies all propertyChangeListeners. One of those listeners should be registered in your Table, and its propertyChanged method should be implemented with refreshing of the table (hd1) had a good answer.
myTableModel.fireTableDataChanged(); should be just enough to force a refresh of your table. As usual, if you have further problems, do feel free to leave a comment.
For a more general solution you can use the Row Table Model and just implement the getValueAt() and setValueAt() methods.
Here should be some code that will be firing my table after adding new row!
The model is responsible for invoking the proper fireXXX method.
Use this type of casting.
((AbstractTableModel)student.getModel()).fireTableCellUpdated();
I have a problem with the following code. My task is, I have to have radio buttons in the first column and when a user selects that radio button that row is selected and sent for processing. But my problem is, I am able to select the radio button which are in the first column, but afterwards when user clicks in any part of the table then my clicked radio button is being unchecked. I am not able to figure, why it is happeneing. I am really stuck with this problem. Help required. The following code shows my problem.
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
public class DisplayTable extends JDialog {
public void initialize() {
SourceTableModel stm = new SourceTableModel();
JTable sourceTable = new JTable(stm);
sourceTable.getColumnModel().getColumn(0).setCellRenderer(new RadioButtonRenderer());
sourceTable.getColumnModel().getColumn(0).setCellEditor(new RadioButtonEditor(new JCheckBox ()));
JPanel panel = new JPanel();
panel.add(new JScrollPane(sourceTable));
add(panel);
setModal(true);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DisplayTable().initialize();
}
});
}
}
class SourceTableModel extends AbstractTableModel{
private static final long serialVersionUID = 1L;
private List<SourceModel> sourceList = new ArrayList<SourceModel>();
private String[] columnNamesList = {"Select", "Group", "Work"};
public SourceTableModel() {
this.sourceList = getSourceDOList();
}
public String getColumnName(int column) {
return columnNamesList[column];
}
public int getRowCount() {
return sourceList.size();
}
public int getColumnCount() {
return columnNamesList.length;
}
public Class<?> getColumnClass(int columnIndex) {
return (columnIndex == 0 ? Boolean.class : String.class);
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return (columnIndex == 0 ? true : false);
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
SourceModel model = (SourceModel) sourceList.get(rowIndex);
switch (columnIndex) {
case 0:
model.setSelect((Boolean)aValue);
break;
case 1:
model.setFactory((String) aValue);
break;
case 2:
model.setSupplier((String) aValue);
break;
}
fireTableCellUpdated(rowIndex, columnIndex);
}
public Object getValueAt(int rowIndex, int columnIndex) {
SourceModel source = sourceList.get(rowIndex);
switch(columnIndex){
case 0:
return source.isSelect();
case 1:
return source.getFactory();
case 2:
return source.getSupplier();
default:
return null;
}
}
private List<SourceModel> getSourceDOList() {
List<SourceModel> tempSourceList=new ArrayList<SourceModel>();
for (int index = 0; index < 5; index++) {
SourceModel source = new SourceModel();
source.setSelect(false);
source.setFactory("group");
source.setSupplier("Work");
tempSourceList.add(source);
}
return tempSourceList;
}
}
class SourceModel {
private boolean select;
private String factory;
private String supplier;
public SourceModel() {
// No Code;
}
public SourceModel(boolean select, String factory, String supplier) {
super();
this.select = select;
this.factory = factory;
this.supplier = supplier;
}
public boolean isSelect() {
return select;
}
public void setSelect(boolean select) {
this.select = select;
}
public String getFactory() {
return factory;
}
public void setFactory(String factory) {
this.factory = factory;
}
public String getSupplier() {
return supplier;
}
public void setSupplier(String supplier) {
this.supplier = supplier;
}
}
class RadioButtonEditor extends DefaultCellEditor implements ItemListener {
public JRadioButton btn = new JRadioButton();
public RadioButtonEditor(JCheckBox checkBox) {
super(checkBox);
}
public Component getTableCellEditorComponent(JTable table, Object
value, boolean isSelected, int row, int column) {
if (value==null)
return null;
btn.addItemListener(this);
if (( (Boolean) value).booleanValue())
btn.setSelected(true);
else
btn.setSelected(false);
return btn;
}
public Object getCellEditorValue() {
if(btn.isSelected() == true)
return new Boolean(true);
else
return new Boolean(false);
}
public void itemStateChanged(ItemEvent e) {
super.fireEditingStopped();
}
}
class RadioButtonRenderer implements TableCellRenderer {
public JRadioButton btn = new JRadioButton();
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (value==null) return null;
if(((Boolean)value).booleanValue())
btn.setSelected(true);
else
btn.setSelected(false);
if (isSelected) {
btn.setForeground(table.getSelectionForeground());
btn.setBackground(table.getSelectionBackground());
} else {
btn.setForeground(table.getForeground());
btn.setBackground(table.getBackground());
}
return btn;
}
}
EDIT:
I have updated my code and I have used Boolean class for the first column. The problem which I am facing is, if I remove super.fireEditingStopped(); from RadioButtonEditor class then I am able to check and then if I click at any part of the table then checked one I being unchecked. If I keep the super.fireEditingStopped(); then I am not even able to check the Radio button.
I know that super.fireEditingStopped(); will stop editing. But my question is how to check it?
P.S: Sorry I have posted my entire code. I thought it will be easy for some one to look at the problem.
This is the screen shot of the program.
From your illustration, it appears that you want to enforce mutual exclusion among the rows of a JTable, where each row has a single JRadioButton. As a ButtonGroup is unsuitable, this example due to #Guillaume Polet uses a custom manager.
I have a problem with the following code. My task is, I have to have
radio buttons in the first column and when a user selects that radio
button that row is selected and sent for processing. But my problem
is, I am able to select the radio button which are in the first
column, but afterwards when user clicks in any part of the table then
my clicked radio button is being unchecked. I am not able to figure,
why it is happeneing. I am really stuck with this problem. Help
required. The following code shows my problem.
don't to use JRadioButton, use built_in support for Boolean value in JTable == JCheckBox,
then you can sorting and filtering based on Boolean value
otherwise you have to override to String ("true" / "false")
there are a few good JRadioButtons as Renderer and Editor in JTable, including usage of JComboBox as Editor for RadioButtonGroup
If you need to dynamically change the Look and Feel, your CellEditor is recommended to extend Component.
//#see javax/swing/SwingUtilities.java
static void updateRendererOrEditorUI(Object rendererOrEditor) {
if (rendererOrEditor == null) {
return;
}
Component component = null;
if (rendererOrEditor instanceof Component) {
component = (Component)rendererOrEditor;
}
if (rendererOrEditor instanceof DefaultCellEditor) {
//Ahh, AbstractCellEditor ...
component = ((DefaultCellEditor)rendererOrEditor).getComponent();
}
if (component != null) {
SwingUtilities.updateComponentTreeUI(component);
}
}
Here is a "CellEditor extends JRadioButton ..." example:
import java.awt.*;
import java.awt.event.*;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
public class DisplayTable2 extends JDialog {
public void initialize() {
Object[][] data = {
{ true, "Group1", "Work1" }, { false, "Group2", "Work2" },
{ false, "Group3", "Work3" }, { false, "Group4", "Work4" }
};
JTable sourceTable = new JTable(new SourceTableModel(data));
sourceTable.getColumnModel().getColumn(0).setCellRenderer(new RadioButtonRenderer());
sourceTable.getColumnModel().getColumn(0).setCellEditor(new RadioButtonEditor());
JPanel panel = new JPanel();
panel.add(new JScrollPane(sourceTable));
add(panel);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setModal(true);
pack();
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
new DisplayTable2().initialize();
}
});
}
}
class SourceTableModel extends DefaultTableModel {
private static final String[] columnNamesList = {"Select", "Group", "Work"};
public SourceTableModel(Object[][] data) {
super(data, columnNamesList);
}
#Override public Class<?> getColumnClass(int columnIndex) {
return (columnIndex == 0 ? Boolean.class : String.class);
}
#Override public boolean isCellEditable(int rowIndex, int columnIndex) {
return (columnIndex == 0 ? true : false);
}
#Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if(columnIndex==0 && aValue instanceof Boolean) {
//lazy development
for(int i=0; i<getRowCount(); i++) {
super.setValueAt(i==rowIndex, i, columnIndex);
}
} else {
super.setValueAt(aValue, rowIndex, columnIndex);
}
}
}
class RadioButtonRenderer extends JRadioButton implements TableCellRenderer {
#Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if(value instanceof Boolean) {
setSelected((Boolean)value);
}
return this;
}
}
class RadioButtonEditor extends JRadioButton implements TableCellEditor {
public RadioButtonEditor() {
super();
addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
fireEditingStopped();
}
});
}
#Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
if(value instanceof Boolean) {
setSelected((Boolean)value);
}
return this;
}
#Override public Object getCellEditorValue() {
return isSelected();
}
//Copid from AbstractCellEditor
//protected EventListenerList listenerList = new EventListenerList();
//transient protected ChangeEvent changeEvent = null;
#Override public boolean isCellEditable(EventObject e) {
return true;
}
#Override public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
#Override public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
#Override public void cancelCellEditing() {
fireEditingCanceled();
}
#Override public void addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class, l);
}
#Override public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listenerList.getListeners(CellEditorListener.class);
}
protected void fireEditingStopped() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingStopped(changeEvent);
}
}
}
protected void fireEditingCanceled() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for(int i = listeners.length-2; i>=0; i-=2) {
if(listeners[i]==CellEditorListener.class) {
// Lazily create the event:
if(changeEvent == null) changeEvent = new ChangeEvent(this);
((CellEditorListener)listeners[i+1]).editingCanceled(changeEvent);
}
}
}
}