I am trying to format a phone number column in a JTable
as (Area) Prefix-Number
. I am not sure what I am doing wrong as it compiles without errors and according to the getTableCellRenderer
tutorials and other numerous examples I've pored over it should work. But it doesn't... it is still formatted as one string in the JTable.
I first accept user input from one text field for a phone number:
//import necessary API
public class AddPatientDialog extends JDialog {
private final JPanel contentPanel = new JPanel();
private JTextField txtFieldPhneNum;
private PatientDAO patientDAO;
private PatientSessionTrackerApp patientSessionTrackerApp;
public AddPatientDialog(PatientSessionTrackerApp thePatientSessionTracker, PatientDAO thePatientDAO) {
this();
patientDAO = thePatientDAO;
patientSessionTrackerApp = thePatientSessionTracker;
}
public final class LengthRestrictedDocument extends PlainDocument {
private final int limit;
public LengthRestrictedDocument(int limit) {
this.limit = limit;
}
@Override
public void insertString(int offs, String str, AttributeSet a)
throws BadLocationException {
if (str == null)
return;
if ((getLength() + str.length()) <= limit) {
super.insertString(offs, str, a);
}
}
}
/**
* Create the dialog.
*/
public AddPatientDialog() {
setSize(450, 300);
setResizable(false);
setTitle("Add New Patient");
getContentPane().setLayout(new BorderLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
contentPanel.setLayout(null);
{
JLabel lblPhneNum = new JLabel("Phone Number");
lblPhneNum.setBounds(10, 86, 104, 14);
contentPanel.add(lblPhneNum);
}
txtFieldPhneNum = new JTextField();
txtFieldPhneNum.setBounds(124, 83, 86, 20);
contentPanel.add(txtFieldPhneNum);
txtFieldPhneNum.setColumns(10);
txtFieldPhneNum.setDocument(new LengthRestrictedDocument(10));
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
JButton okButton = new JButton("Add Patient");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
savePatient();
}
});
okButton.setActionCommand("OK");
buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton);
}
JTextArea txtAddPatientWarning = new JTextArea();
txtAddPatientWarning.setText("WARNING!!! Once a patient is created it is added to the database and cannot be removed from the database via the Patient Session Tracker. If a patient is added in error, please contact Sudo Technologies at (701) 388-3752.");
txtAddPatientWarning.setBounds(10, 132, 424, 60);
txtAddPatientWarning.setEditable(false);
txtAddPatientWarning.setBackground(Color.YELLOW);
txtAddPatientWarning.setLineWrap(true);
contentPanel.add(txtAddPatientWarning);
JLabel lblIe = new JLabel("Enter numbers only (i.e. 1234567890)");
lblIe.setBounds(220, 86, 214, 14);
contentPanel.add(lblIe);
}
protected void savePatient() {
// get the patient info from the gui
String firstName = txtFieldFName.getText();
String middleName = txtFieldMName.getText();
String lastName = txtFieldLName.getText();
String phneNum = txtFieldPhneNum.getText();
String email = txtFieldEmail.getText();
Patient tempPatient = new Patient(phneNum);
if ((firstName.trim().length() != 0) && (lastName.trim().length() != 0)) {
try{
// save to the database
patientDAO.addPatient(tempPatient);
// close dialog
setVisible(false);
dispose();
// refresh gui list
patientSessionTrackerApp.refreshPatientsView();
// show success message
JOptionPane.showMessageDialog(patientSessionTrackerApp, "Patient added to the database.",
"Patient Added",
JOptionPane.INFORMATION_MESSAGE);
} catch (Exception exc) {
JOptionPane.showMessageDialog(patientSessionTrackerApp,
"Error saving patient: " +
exc.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
}
} else
JOptionPane.showMessageDialog(patientSessionTrackerApp, "<html>Please fill in all required fields as denoted by the <font color='red'>*</font>.</html>");
}
}
I then try to pass it into a PhoneNumber class in order to try and parse the string:
public class PhoneNumber {
public String areaCode;
public String prefix;
public String num;
public PhoneNumber(String areaCode, String prefix, String num) {
this.areaCode = areaCode;
this.prefix = prefix;
this.num = num;
}
public PhoneNumber() {}
public PhoneNumber parsePhoneNumber(String phneNum) {
String[] phneNumArr = {phneNum.substring(0,3), phneNum.substring(3,6), phneNum.substring(6)};
areaCode = phneNumArr[0];
prefix = phneNumArr[1];
num = phneNumArr[2];
return new PhoneNumber(areaCode, prefix, num);
}
@Override
public String toString() {
return "areaCode=" + areaCode + "; prefix=" + prefix + "; num=" + num;
}
}
From there I try to use the Object parser to format it in a CellRenderer
class:
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import com.patientsessiontracker.core.*;
public class PatientSessionTrackerCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public String areaCode;
public String prefix;
public String num;
public String phneNum;
public Component getTableCellRendererComponent(JTable table, PhoneNumber value, boolean isSelected,
boolean hasFocus, int row, int column) {
//Component cellComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
PhoneNumber phneNum = new PhoneNumber();
value = phneNum;
String text = "(" + phneNum.areaCode + ") " + phneNum.prefix + "-" + phneNum.num;
setHorizontalAlignment(SwingConstants.CENTER);
setText(text);
return this;
}
}
Finally I attempt to implement the new format in the JTable:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import java.util.List;
import com.patientsessiontracker.core.*;
import com.patientsessiontracker.dao.*;
public class PatientSessionTrackerApp extends JFrame {
private JPanel contentPane;
private JTextField txtFieldSrchFName;
private JButton btnSearch;
private JScrollPane scrollPane;
private static JTable table;
private static PatientDAO patientDAO;
private JPanel panelBtn;
private JButton btnAddPatient;
private JTextField txtFieldSrchLName;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
PatientSessionTrackerApp frame = new PatientSessionTrackerApp();
frame.setVisible(true);
PatientSessionTrackerApp.refreshPatientsView();
table.getColumnModel().getColumn(3).setCellRenderer(new PatientSessionTrackerCellRenderer());
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public PatientSessionTrackerApp() {
// create the DAO
try {
patientDAO = new PatientDAO();
} catch (Exception exc) {
JOptionPane.showMessageDialog(this, "Error: " + exc, "Error", JOptionPane.ERROR_MESSAGE);
}
setTitle("Patient Session Tracker");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(600, 300);
setResizable(false);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JPanel panelSrch = new JPanel();
panelSrch.setBounds(5, 5, 579, 33);
contentPane.add(panelSrch);
panelSrch.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
JLabel lblFirstName = new JLabel("First Name");
panelSrch.add(lblFirstName);
txtFieldSrchFName = new JTextField();
panelSrch.add(txtFieldSrchFName);
txtFieldSrchFName.setColumns(13);
btnSearch = new JButton("Search");
btnSearch.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String firstName = txtFieldSrchFName.getText();
String lastName = txtFieldSrchLName.getText();
List<Patient> patients = null;
if (((firstName != null) && (firstName.trim().length() > 0)) ||
((lastName != null) && (lastName.trim().length() > 0)))
patients = patientDAO.searchPatients(firstName, lastName);
else
patients = patientDAO.getAllPatients();
// create the model and update the "table"
PatientTableModel model = new PatientTableModel(patients);
table.setModel(model);
table.getColumnModel().getColumn(3).setCellRenderer(new PatientSessionTrackerCellRenderer());
} catch (Exception exc) {
JOptionPane.showMessageDialog(PatientSessionTrackerApp.this, "Error: " + exc, "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
JLabel lblLastName = new JLabel("Last Name");
panelSrch.add(lblLastName);
txtFieldSrchLName = new JTextField();
panelSrch.add(txtFieldSrchLName);
txtFieldSrchLName.setColumns(13);
panelSrch.add(btnSearch);
scrollPane = new JScrollPane();
scrollPane.setBounds(5, 38, 579, 186);
contentPane.add(scrollPane);
table = new JTable();
scrollPane.setViewportView(table);
panelBtn = new JPanel();
panelBtn.setBounds(5, 224, 579, 38);
contentPane.add(panelBtn);
btnAddPatient = new JButton("Add Patient");
btnAddPatient.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// create dialog
AddPatientDialog addPatient = new AddPatientDialog(PatientSessionTrackerApp.this, patientDAO);
// show dialog
addPatient.setModal(true);
addPatient.setVisible(true);
}
});
panelBtn.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
panelBtn.add(btnAddPatient);
}
public static void refreshPatientsView() {
try {
List<Patient> patients = patientDAO.getAllPatients();
PatientTableModel model = new PatientTableModel(patients);
table.setModel(model);
} catch (Exception exc) {
JOptionPane.showMessageDialog(null, "Error: " + exc, "Error",
JOptionPane.ERROR_MESSAGE);
}
}
}
The phone number gets stored and retrieved from a tinytext
column in the MySQL database
. Feel free to ask for more code if needed, I hacked a lot of parts out to try and shorten the question.
Okay... I am completely lost and admittedly a noob to Java Swing.I tried invoking the class Phone Number into the Custom CellRenderer Class. What I get for a result is (null) null-null. I'm not sure what I am doing wrong, should I try using the MaskFormatter API as shown in the CustomCellRenderer tutorial? Here is the refractored code:
CustomCellRenderer:
public class PatientSessionTrackerCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
public PatientSessionTrackerCellRenderer() {
super();
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public void setValue(Object value) {
PhoneNumber phneNum = new PhoneNumber();
value.toString();
value = "(" + phneNum.areaCode + ") " + phneNum.prefix + "-" + phneNum.num;
super.setValue(value);
}
}
PhoneNumber Class:
public class PhoneNumber {
public String areaCode;
public String prefix;
public String num;
private PhoneNumber(String areaCode, String prefix, String num) {
this.areaCode = areaCode;
this.prefix = prefix;
this.num = num;
}
public PhoneNumber() {}
public PhoneNumber parsePhoneNumber(String value) {
String[] phneNumArr = {value.substring(0,3), value.substring(3,6), value.substring(6)};
areaCode = phneNumArr[0];
prefix = phneNumArr[1];
num = phneNumArr[2];
return new PhoneNumber(areaCode, prefix, num);
}
@Override
public String toString() {
return "areaCode=" + areaCode + "; prefix=" + prefix + "; num=" + num;
}
}
it compiles without errors and according to the getTableCellRenderer tutorials and other numerous examples I've poured over it should work
The problem is that you haven't implemented the renderer correctly. I suggest you start with the Swing tutorial on Using Custom Renderers for proper examples.
The method you created is never invoked. The getTableCellRendererComponent(...)
method of the DefaultTableCellRenderer does not have a PhoneNumber
as a parameter. It has an Object
.
Whenever you attempt to override a method don't forget the @Override
annotation:
@Override
getTableCellRendererComponent(JTable table, PhoneNumber value, boolean isSelected, boolean hasFocus, int row, int column)
Now try to compile your code and you will get an error because the method signature of the method you are attempting to override does not exist.
To format the data in a renderer the easier approach is override the setValue(...)
method of the renderer. The code would be something like:
public class PhoneNumberRenderer extends DefaultTableCellRenderer
{
public PhoneNumberRenderer()
{
super();
setHorizontalAlignment(SwingConstants.CENTER);
}
@Override
public void setValue(Object value)
{
if (value instanceof PhoneNumber)
{
PhoneNumber pn = (PhoneNumber)value;
value = "(" + pn.areaCode + ") " + pn.prefix" + "...";
}
super.setValue(value);
}
}
You may want to create a getFormattedPhoneNumber()
method in your PhoneNumber class to return the phone number in a formatted String. Other classes should not really be accessing the variables directly and formatting the data in a single place is always a good idea.