Search code examples
javaswingawtjtextfield

How to make a Round Rectangle JTextField?


I want to make a round rectangle JTextField. I write a sub class of AbstractBorder to realize it.But I run into some problems. My requirement is: enter image description here

What I get is:

enter image description here

My code is :

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.AbstractBorder;
import javax.swing.border.EmptyBorder;


public class JTextFieldTest {
    JTextField textField;
    boolean activate = false;

    public void createUI(){
        JFrame frame = new JFrame("Test JTextField");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(true);

        MainPanel mainPanel = new MainPanel();
        frame.add(mainPanel,BorderLayout.CENTER);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        JTextFieldTest jTextFieldTest = new JTextFieldTest();
        jTextFieldTest.createUI();
    }

    @SuppressWarnings("serial")
    class MainPanel extends JPanel{
        public MainPanel(){

            textField = new JTextField("Please input:");
            Font fieldFont = new Font("Arial", Font.PLAIN, 20);
            textField.setFont(fieldFont);
            textField.setBackground(Color.white);
            textField.setForeground(Color.gray.brighter());

            textField.setColumns(30);
            textField.setBorder(BorderFactory.createCompoundBorder(new CustomeBorder(), 
new EmptyBorder(new Insets(10, 20, 10, 20))));
            textField.addActionListener(new FieldListener());
            textField.addMouseListener(new FieldMouseListener());

            add(textField,BorderLayout.CENTER);
            setBackground(Color.blue);
            setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        }
    }

    @SuppressWarnings("serial")
    class CustomeBorder extends AbstractBorder{
        @Override
        public void paintBorder(Component c, Graphics g, int x, int y,
                int width, int height) {
            // TODO Auto-generated method stub
            super.paintBorder(c, g, x, y, width, height);
            g.setColor(Color.black);
            g.drawRoundRect(x, y, width, height, 20, 20);
        }

    }

    class FieldListener implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent e) {
            // TODO Auto-generated method stub
            System.out.println(textField.getText());
        }

    }

    class FieldMouseListener implements MouseListener{
        @Override
        public void mouseClicked(MouseEvent e) {
            // TODO Auto-generated method stub
            if(activate == false){
                textField.setText("");
            }
            activate = true;
            textField.setForeground(Color.black);


        }

        @Override
        public void mousePressed(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseReleased(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseEntered(MouseEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void mouseExited(MouseEvent e) {
            // TODO Auto-generated method stub

        }
    }
}

I can get another effect by using

textField.setOpaque(false);

g.setColor(Color.white); g.drawRoundRect(x, y, width - 1, height - 1, 20, 20);

Another effect is: enter image description here

In sum, what can I do to realize my requirement?

@Arijit, it's the effect of your solution when it runs on my computer. enter image description here enter image description here


Solution

  • Use a TextBubbleBorder.

    enter image description here

    import java.awt.*;
    import java.awt.geom.*;
    import javax.swing.*;
    import javax.swing.border.AbstractBorder;
    
    class TextBubbleBorder extends AbstractBorder {
    
        private Color color;
        private int thickness = 4;
        private int radii = 8;
        private int pointerSize = 7;
        private Insets insets = null;
        private BasicStroke stroke = null;
        private int strokePad;
        private int pointerPad = 4;
        RenderingHints hints;
    
        TextBubbleBorder(
            Color color) {
                this(color, 4, 8, 7);
        }
    
        TextBubbleBorder(
            Color color, int thickness, int radii, int pointerSize) {
                this.thickness = thickness;
                this.radii = radii;
                this.pointerSize = pointerSize;
            this.color = color;
    
            stroke = new BasicStroke(thickness);
            strokePad = thickness/2;
    
            hints = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
    
            int pad = radii + strokePad;
            int bottomPad = pad + pointerSize + strokePad;
            insets = new Insets(pad,pad,bottomPad,pad);
        }
    
        @Override
        public Insets getBorderInsets(Component c) {
            return insets;
        }
    
        @Override
        public Insets getBorderInsets(Component c, Insets insets) {
            return getBorderInsets(c);
        }
    
        @Override
        public void paintBorder(
            Component c,
            Graphics g,
            int x, int y,
            int width, int height) {
    
            Graphics2D g2 = (Graphics2D)g;
    
            int bottomLineY = height-thickness-pointerSize;
    
            RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(
                0+strokePad,
                0+strokePad,
                width-thickness,
                bottomLineY,
                radii,
                radii
                );
    
            Polygon pointer = new Polygon();
    
            // left point
            pointer.addPoint(
                strokePad+radii+pointerPad,
                bottomLineY);
            // right point
            pointer.addPoint(
                strokePad+radii+pointerPad+pointerSize,
                bottomLineY);
            // bottom point
            pointer.addPoint(
                strokePad+radii+pointerPad+(pointerSize/2),
                height-strokePad);
    
            Area area = new Area(bubble);
            area.add(new Area(pointer));
    
            g2.setRenderingHints(hints);
    
            Area spareSpace = new Area(new Rectangle(0,0,width,height));
            spareSpace.subtract(area);
            g2.setClip(spareSpace);
            g2.clearRect(0,0,width,height);
            g2.setClip(null);
    
            g2.setColor(color);
            g2.setStroke(stroke);
            g2.draw(area);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    JTextField o = new JTextField(
                        "The quick brown fox jumps over the lazy dog!");
    
                    o.setBorder(new TextBubbleBorder(Color.MAGENTA.darker(),2,4,0));
                    JOptionPane.showMessageDialog(null, o);
                }
            });
        }
    }