2

I have developed a swing form which validates entries as the focus is lost from JTextComponent and if the entry is correct ,the background is painted green Otherwise it is painted red.

Here is the code :-

import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;



public class Form extends JFrame implements FocusListener{
JTextField fname,lname,phone,email,address;
BufferedImage img;
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){public void run(){new Form();}});
}

public Form()
{
super("Form with Validation Capability");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,300);
setLayout(new GridLayout(5,3));

try {
    img=ImageIO.read(new File("src/warning.png"));
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

add(new JLabel("First Name :"));
add(fname=new JTextField(20));
add(new JLabel("Last Name :"));
add(lname=new JTextField(20));
add(new JLabel("Phone Number :"));
add(phone=new JTextField(10));
add(new JLabel("Email:"));
add(email=new JTextField(20));
add(new JLabel("Address :"));
add(address=new JTextField(20));

fname.addFocusListener(this);
lname.addFocusListener(this);
phone.addFocusListener(this);
email.addFocusListener(this);
address.addFocusListener(this);

setVisible(true);
}

public void focusGained(FocusEvent e)
{
((JTextComponent) e.getSource()).setBackground(Color.WHITE);
}
public void focusLost(FocusEvent e)
{
if(e.getSource().equals(fname))
{
    validationForText(fname);
}
else if(e.getSource().equals(lname))
{
    validationForText(lname);
}
else if(e.getSource().equals(email))
{
    validationForEmail(email);
}
else if(e.getSource().equals(phone))
{
    validationForNumber(phone);
}
else{
    ((JTextComponent) e.getSource()).setBackground(Color.GREEN);
}
}
public void validationForText(JTextComponent comp)
{
String temp=comp.getText();
if (temp.matches("^[A-Za-z]+$")) {
    comp.setBackground(Color.GREEN);
}
else
    comp.setBackground(Color.RED);
}
public void validationForNumber(JTextComponent comp)
{
String text=comp.getText();
if(text.matches("^[0-9]+$"))
    comp.setBackground(Color.GREEN);
else
    comp.setBackground(Color.RED);
}
public void validationForEmail(JTextComponent comp)
{
String text=comp.getText();
if(text.matches("[^@]+@([^.]+\\.)+[^.]+"))
    comp.setBackground(Color.GREEN);
else
    comp.setBackground(Color.RED);
}

}

Here is the output to my simple app :

enter image description here

Now I also want to draw a warning symbol (buffered image) at the lower left corner of incorrect field.Can anyone help me how to do this as i am not understanding where to paint the image. (I would like to do it using JLayeredPane as I am learning its application,any code snippet will really help).

David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
Naveen
  • 7,944
  • 12
  • 78
  • 165

1 Answers1

8

Why not simply add a JLabel (using an appropriate LayoutManager to position it) with the inbuilt Swing warning icon UIManager.getIcon("OptionPane.warningIcon")

enter image description here

like so:

JLabel label=new JLabel(UIManager.getIcon("OptionPane.warningIcon"));

see here for more.

UPDATE:

As per your comment I would rather use GlassPane (and add JLabel to glasspane) for this as opposed too JLayeredPane. See here for more on RootPanes

Some suggestions:

  • Dont extend JFrame unnecessarily

  • Dont call setSize on JFrame. Use a correct LayoutManager and/or override getPreferredSize() where necessary and replace setSize call with pack() just before setting JFrame visible.

  • Dont implement FocusListener on the class unless you want to share the functionality with other classes.

  • Also get into the habit of using FocusAdapter vs FocusListener this applies to a few with appended Listener i.e MouseListener can be replaced for MouseAdapter. This allows us to only override the methods we want. In this case listener is correct as we do something on both overridden methods, but as I said more about the habit

  • Also always call super.XXX implementation of overridden methods i.e focusGained(FocusEvent fe) unless you are ignoring it for a reason

Here is code with above fixes including glasspane to show warning icon when field is red:

enter image description here

And removes it when is corrected:

enter image description here

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;

/**
 *
 * @author David
 */
public class GlassValidationPane extends JComponent {

    private static JTextField fname, lname, phone, email, address;
    private static GlassValidationPane gvp = new GlassValidationPane();

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {

                JFrame f = new JFrame("Form with Validation Capability");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //f.setResizable(false);
                f.setLayout(new GridLayout(5, 3));

                f.add(new JLabel("First Name :"));
                f.add(fname = new JTextField(20));
                f.add(new JLabel("Last Name :"));
                f.add(lname = new JTextField(20));
                f.add(new JLabel("Phone Number :"));
                f.add(phone = new JTextField(10));
                f.add(new JLabel("Email:"));
                f.add(email = new JTextField(20));
                f.add(new JLabel("Address :"));
                f.add(address = new JTextField(20));

                f.addComponentListener(new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent ce) {
                        super.componentResized(ce);
                        gvp.refreshLocations();
                    }
                });
                FocusAdapter fl = new FocusAdapter() {
                    @Override
                    public void focusGained(FocusEvent fe) {
                        super.focusGained(fe);
                        ((JTextComponent) fe.getSource()).setBackground(Color.WHITE);
                    }

                    @Override
                    public void focusLost(FocusEvent fe) {
                        super.focusLost(fe);
                        if (fe.getSource().equals(fname)) {
                            validationForText(fname);
                        } else if (fe.getSource().equals(lname)) {
                            validationForText(lname);
                        } else if (fe.getSource().equals(email)) {
                            validationForEmail(email);
                        } else if (fe.getSource().equals(phone)) {
                            validationForNumber(phone);
                        } else {
                            gvp.removeWarningIcon(((Component) fe.getSource()));
                            ((JTextComponent) fe.getSource()).setBackground(Color.GREEN);
                        }
                    }
                };

                fname.addFocusListener(fl);
                lname.addFocusListener(fl);
                phone.addFocusListener(fl);
                email.addFocusListener(fl);
                address.addFocusListener(fl);

                f.setGlassPane(gvp);

                f.pack();
                f.setVisible(true);
                gvp.setVisible(true);
            }

            public void validationForText(JTextComponent comp) {
                String temp = comp.getText();
                if (temp.matches("^[A-Za-z]+$")) {
                    setGreen(comp);
                } else {
                    setRed(comp);
                }
            }

            public void validationForNumber(JTextComponent comp) {
                String text = comp.getText();
                if (text.matches("^[0-9]+$")) {
                    setGreen(comp);
                } else {
                    setRed(comp);
                }
            }

            public void validationForEmail(JTextComponent comp) {
                String text = comp.getText();
                if (text.matches("[^@]+@([^.]+\\.)+[^.]+")) {
                    setGreen(comp);
                } else {
                    setRed(comp);
                }
            }

            private void setRed(JTextComponent comp) {
                comp.setBackground(Color.RED);
                gvp.showWarningIcon(comp);
            }

            private void setGreen(JTextComponent comp) {
                comp.setBackground(Color.GREEN);
                gvp.removeWarningIcon(comp);

            }
        });
    }
    private HashMap<Component, JLabel> warningLabels = new HashMap<>();
    private ImageIcon warningIcon;

    public GlassValidationPane() {
        setLayout(null);//this is the exception to the rule case a layoutmanager might make setting Jlabel co-ords harder
        setOpaque(false);
        Icon icon = UIManager.getIcon("OptionPane.warningIcon");
        int imgW = icon.getIconWidth();
        int imgH = icon.getIconHeight();
        BufferedImage img = ImageUtilities.getBufferedImageOfIcon(icon, imgW, imgH);
        warningIcon = new ImageIcon(ImageUtilities.resize(img, 24, 24));
    }

    void showWarningIcon(Component c) {
        if (warningLabels.containsKey(c)) {
            return;
        }

        JLabel label = new JLabel();
        label.setIcon(warningIcon);

        //int x=c.getX();//this will make it insode the component
        int x = c.getWidth() - label.getIcon().getIconWidth();//this makes it appear outside/next to component
        int y = c.getY();

        label.setBounds(x, y, label.getIcon().getIconWidth(), label.getIcon().getIconHeight());
        add(label);
        label.setVisible(true);
        revalidate();
        repaint();
        warningLabels.put(c, label);
    }

    public void removeWarningIcon(Component c) {
        for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
            Component component = entry.getKey();
            JLabel jLabel = entry.getValue();
            if (component == c) {
                remove(jLabel);
                revalidate();
                repaint();
                break;
            }
        }
        warningLabels.remove(c);
    }

    public void refreshLocations() {
        for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
            Component c = entry.getKey();
            JLabel label = entry.getValue();
            //int x=c.getX();//this will make it insode the component
            int x = c.getWidth() - label.getIcon().getIconWidth();//this makes it appear outside/next to component
            int y = c.getY();

            label.setBounds(x, y, label.getIcon().getIconWidth(), label.getIcon().getIconHeight());
            revalidate();
            repaint();
        }
    }
}

class ImageUtilities {

    public static BufferedImage resize(BufferedImage image, int width, int height) {
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
        Graphics2D g2d = (Graphics2D) bi.createGraphics();
        g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
        g2d.drawImage(image, 0, 0, width, height, null);
        g2d.dispose();
        return bi;
    }

    public static BufferedImage getBufferedImageOfIcon(Icon icon, int imgW, int imgH) {
        BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D) img.getGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        icon.paintIcon(null, g2d, 0, 0);
        g2d.dispose();
        return img;
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • :I want to learn how to use JLayeredPane for such tasks, that's why I want to use an image. – Naveen Jan 05 '13 at 09:08
  • @Naveen Hmm well I dont `JLayeredPane` is necessary for this see update. IMO you are over complicating a simple task in hopes to learn JLayeredPane, rather learn JLayeredPane by making an application which actually requires it – David Kroukamp Jan 05 '13 at 09:12
  • 3
    +1 for `Icon`. Also consider doing this in the `shouldYieldFocus()` method of an `InputVerifier`, mentioned [here](http://stackoverflow.com/a/14170275/230513). – trashgod Jan 05 '13 at 09:14
  • @trashgod thank you, and thats something I remembered from your advice :) – David Kroukamp Jan 05 '13 at 09:16
  • 2
    @naveen You might light to have look at [Validation overlays using JXLayer](http://www.pushing-pixels.org/2007/08/01/validation-overlays-using-jxlayer.html) and [Validaion overlays using glass pane](http://www.pushing-pixels.org/2007/08/02/validation-overlays-using-glass-pane.html) - nb JXLayer should be replaceable by JLayer on Java 7 – MadProgrammer Jan 05 '13 at 09:23
  • @MadProgrammer +1 for `JXLayer`, but besides that do you propose using JLayer or GlassPane? or both? – David Kroukamp Jan 05 '13 at 09:26
  • @david a lot comes down to what it is to want to achieve. The problem I have with using the glass pane (personally) is in large applications, it can be used for a number of things (we use it as a message popup layer). JLayer is essentially a GlassPane for components which means you can configure it per component, making it far more flexible - IMHO – MadProgrammer Jan 05 '13 at 09:47
  • 1
    Wow this is fabulous, I am amazed with the POWER of Swing today :-) – nIcE cOw Jan 05 '13 at 13:07
  • 1
    @GagandeepBali Thank you and yes Swing is awesome never thought it would be this simple when creating it:) – David Kroukamp Jan 05 '13 at 13:10
  • @Naveen please see update fixed 2 issues with code (added calls to `revalidate()` and `repaint()` after removing `JLabel` and made icons bigger for better visuals) – David Kroukamp Jan 05 '13 at 21:13