Decorating a JTextField with an image and hint

前端 未结 2 2006
轮回少年
轮回少年 2020-12-14 13:04

I\'m trying to create some nicer looking JTextFields with an image and a hint. To do this I made a decorator that overrides the paintComponent method. The reason I used a de

相关标签:
2条回答
  • 2020-12-14 13:37

    In order to paint an icon inside a text field you need to add some insets. You don't want to hard-code insets in your component but just add a little bit of space for the icon, letting clients and subclasses to set their own.

    In the figure above I painted original insets in green and additional insets in red. First thing you want to extend JTextField. We keep track of two things: the original insets (the green ones) mBorder, and the icon.

    public class IconTextField extends JTextField {
        private Border mBorder;
        private Icon mIcon;
    
        // ...
    }
    

    Then you need to override setBorder() method.

    @Override
    public void setBorder(Border border) {
        mBorder = border;
    
        if (mIcon == null) {
            super.setBorder(border);
        } else {
            Border margin = BorderFactory.createEmptyBorder(0, mIcon.getIconWidth() + ICON_SPACING, 0, 0);
            Border compound = BorderFactory.createCompoundBorder(border, margin);
            super.setBorder(compound);
        }
    }
    

    Here, if we have an icon (the field mIcon is not null), we add our additional insets using a compound border. Then, you should also override the paintComponent() method.

    @Override
    protected void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);
    
        if (mIcon != null) {
            Insets iconInsets = mBorder.getBorderInsets(this);
            mIcon.paintIcon(this, graphics, iconInsets.left, iconInsets.top);
        }
    }
    

    Finally, you need a setIcon() method.

    public void setIcon(Icon icon) {
        mIcon = icon;
        resetBorder();
    }
    
    private void resetBorder() {
        setBorder(mBorder);
    }
    

    What we are doing here is saving the icon and recalculating the borders.

    If you want to do the same same thing with JPasswordField, the most elegant thing is probably to create a helper class with all the methods discussed above.

    class IconTextComponentHelper {
        private static final int ICON_SPACING = 4;
    
        private Border mBorder;
        private Icon mIcon;
        private Border mOrigBorder;
        private JTextComponent mTextComponent;
    
        IconTextComponentHelper(JTextComponent component) {
            mTextComponent = component;
            mOrigBorder = component.getBorder();
            mBorder = mOrigBorder;
        }
    
        Border getBorder() {
            return mBorder;
        }
    
        void onPaintComponent(Graphics g) {
            if (mIcon != null) {
                Insets iconInsets = mOrigBorder.getBorderInsets(mTextComponent);
                mIcon.paintIcon(mTextComponent, g, iconInsets.left, iconInsets.top);
            }
        }
    
        void onSetBorder(Border border) {
            mOrigBorder = border;
    
            if (mIcon == null) {
                mBorder = border;
            } else {
                Border margin = BorderFactory.createEmptyBorder(0, mIcon.getIconWidth() + ICON_SPACING, 0, 0);
                mBorder = BorderFactory.createCompoundBorder(border, margin);
            }
        }
    
        void onSetIcon(Icon icon) {
            mIcon = icon;
            resetBorder();
        }
    
        private void resetBorder() {
            mTextComponent.setBorder(mOrigBorder);
        }
    }
    

    And use it like this:

    public class IconTextField extends JTextField {
        private IconTextComponentHelper mHelper = new IconTextComponentHelper(this);
    
        public IconTextField() {
            super();
        }
    
        public IconTextField(int cols) {
            super(cols);
        }
    
        private IconTextComponentHelper getHelper() {
            if (mHelper == null)
                mHelper = new IconTextComponentHelper(this);
    
            return mHelper;
        }
    
        @Override
        protected void paintComponent(Graphics graphics) {
            super.paintComponent(graphics);
            getHelper().onPaintComponent(graphics);
        }
    
        public void setIcon(Icon icon) {
            getHelper().onSetIcon(icon);
        }
    
        public void setIconSpacing(int spacing) {
            getHelper().onSetIconSpacing(spacing);
        }
    
        @Override
        public void setBorder(Border border) {
            getHelper().onSetBorder(border);
            super.setBorder(getHelper().getBorder());
        }
    }
    
    0 讨论(0)
  • 2020-12-14 13:48

    Text Prompt works with a JPasswordField.

    One difference is that the displayed icon disappears when text is entered. If you want the icon to be permanent then I suggest you create a custom "IconBorder* class to paint an Icon rather then do custom painting in the paintComponent() method.

    You approach will not work unless you duplicate the code for both JTextField and JPasswordField.

    Edit:

    Actually you don't need to create a custom IconBorder. The MatteBorder supports the painting of an Icon in a Border.

    0 讨论(0)
提交回复
热议问题