使用图像和提示装饰 JTextField

发布于 2024-11-09 12:17:07 字数 3207 浏览 0 评论 0原文

我正在尝试使用图像和提示创建一些看起来更漂亮的 JTextFields。为此,我创建了一个重写 PaintComponent 方法的装饰器。我使用装饰器的原因是我想将其应用于其他类型的 JTextField,例如 JPasswordField。

这是我到目前为止所做的;

在此处输入图像描述

左侧表单中看到的问题是,即使我使用了 JPasswordField PaintComponent似乎忽略了我假设的密码paintComponent,它可能会执行密码屏蔽符号。

所以问题是,如何避免重复 JTextFields 和 JPasswordFields 的代码,但仍然具有不同的功能,例如密码屏蔽。

这是装饰器代码;

public class JTextFieldHint extends JTextField implements FocusListener{
private JTextField jtf;
private Icon icon;
private String hint;
private Insets dummyInsets;

public JTextFieldHint(JTextField jtf, String icon, String hint){
    this.jtf = jtf;
    setIcon(createImageIcon("icons/"+icon+".png",icon));
    this.hint = hint;

    Border border = UIManager.getBorder("TextField.border");
    JTextField dummy = new JTextField();
    this.dummyInsets = border.getBorderInsets(dummy);

    addFocusListener(this);
}

public void setIcon(Icon newIcon){
    this.icon = newIcon;
}

@Override
protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int textX = 2;

        if(this.icon!=null){
            int iconWidth = icon.getIconWidth();
            int iconHeight = icon.getIconHeight();
            int x = dummyInsets.left + 5;
            textX = x+iconWidth+2;
            int y = (this.getHeight() - iconHeight)/2;
            icon.paintIcon(this, g, x, y);
        }

        setMargin(new Insets(2, textX, 2, 2));

        if ( this.getText().equals("")) {
            int width = this.getWidth();
            int height = this.getHeight();
            Font prev = g.getFont();
            Font italic = prev.deriveFont(Font.ITALIC);
            Color prevColor = g.getColor();
            g.setFont(italic);
            g.setColor(UIManager.getColor("textInactiveText"));
            int h = g.getFontMetrics().getHeight();
            int textBottom = (height - h) / 2 + h - 4;
            int x = this.getInsets().left;
            Graphics2D g2d = (Graphics2D) g;
            RenderingHints hints = g2d.getRenderingHints();
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.drawString(hint, x, textBottom);
            g2d.setRenderingHints(hints);
            g.setFont(prev);
            g.setColor(prevColor);
        }

}

protected ImageIcon createImageIcon(String path, String description) {
    java.net.URL imgURL = getClass().getResource(path);
    if (imgURL != null) {
        return new ImageIcon(imgURL, description);
    } else {
        System.err.println("Couldn't find file: " + path);
        return null;
    }
}

@Override
public void focusGained(FocusEvent arg0) {
    this.repaint();
}

@Override
public void focusLost(FocusEvent arg0) {
    this.repaint();
}


}

这就是我创建字段的地方;

JTextField usernameField = new JTextFieldHint(new JTextField(),"user_green","Username");
JTextField passwordField = new JTextFieldHint(new JPasswordField(),"bullet_key","Password");

希望我没有完全走错方向!

谢谢!

编辑:我再看一遍,很明显调用 super.paintComponent(g) 将调用 JTextFields PaintComponent,但我不知道如何在不重复代码的情况下解决这个问题。

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 decorator is that I wanted to apply it to other types of JTextField such as JPasswordField.

Here is what I've made so far;

enter image description here

The problem as seen in the form on the left is that, even though I have used a JPasswordField the paintComponent seems to ignore what I assume is the passwords paintComponent which presumably does the password masking symbols.

So the question is, how can I avoid duplicating the code for JTextFields and JPasswordFields but still have the different functionality such as password masking.

This is the decorator code;

public class JTextFieldHint extends JTextField implements FocusListener{
private JTextField jtf;
private Icon icon;
private String hint;
private Insets dummyInsets;

public JTextFieldHint(JTextField jtf, String icon, String hint){
    this.jtf = jtf;
    setIcon(createImageIcon("icons/"+icon+".png",icon));
    this.hint = hint;

    Border border = UIManager.getBorder("TextField.border");
    JTextField dummy = new JTextField();
    this.dummyInsets = border.getBorderInsets(dummy);

    addFocusListener(this);
}

public void setIcon(Icon newIcon){
    this.icon = newIcon;
}

@Override
protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        int textX = 2;

        if(this.icon!=null){
            int iconWidth = icon.getIconWidth();
            int iconHeight = icon.getIconHeight();
            int x = dummyInsets.left + 5;
            textX = x+iconWidth+2;
            int y = (this.getHeight() - iconHeight)/2;
            icon.paintIcon(this, g, x, y);
        }

        setMargin(new Insets(2, textX, 2, 2));

        if ( this.getText().equals("")) {
            int width = this.getWidth();
            int height = this.getHeight();
            Font prev = g.getFont();
            Font italic = prev.deriveFont(Font.ITALIC);
            Color prevColor = g.getColor();
            g.setFont(italic);
            g.setColor(UIManager.getColor("textInactiveText"));
            int h = g.getFontMetrics().getHeight();
            int textBottom = (height - h) / 2 + h - 4;
            int x = this.getInsets().left;
            Graphics2D g2d = (Graphics2D) g;
            RenderingHints hints = g2d.getRenderingHints();
            g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g2d.drawString(hint, x, textBottom);
            g2d.setRenderingHints(hints);
            g.setFont(prev);
            g.setColor(prevColor);
        }

}

protected ImageIcon createImageIcon(String path, String description) {
    java.net.URL imgURL = getClass().getResource(path);
    if (imgURL != null) {
        return new ImageIcon(imgURL, description);
    } else {
        System.err.println("Couldn't find file: " + path);
        return null;
    }
}

@Override
public void focusGained(FocusEvent arg0) {
    this.repaint();
}

@Override
public void focusLost(FocusEvent arg0) {
    this.repaint();
}


}

And this is where I create the fields;

JTextField usernameField = new JTextFieldHint(new JTextField(),"user_green","Username");
JTextField passwordField = new JTextFieldHint(new JPasswordField(),"bullet_key","Password");

Hopefully i've not went completely off in the wrong direction here!

Thanks!

EDIT : Again the more I look at it, it is obvious that calling super.paintComponent(g) is going to call the JTextFields paintcomponent, but I can't see how to solve this without duplicating the code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

思慕 2024-11-16 12:17:07

文本提示与 JPasswordField 配合使用。

一个区别是,输入文本时显示的图标会消失。如果您希望图标是永久的,那么我建议您创建一个自定义的“IconBorder* 类来绘制图标,而不是在 PaintComponent() 方法中进行自定义绘制。

除非您复制 JTextField 和 JPasswordField 的代码,否则您的方法将不起作用 实际上

编辑:

您不需要创建自定义 IconBorder。MatteBorder 支持在边框中绘制图标。

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.

紫﹏色ふ单纯 2024-11-16 12:17:07

为了在文本字段内绘制图标,您需要添加一些插图。
您不想在组件中对插入进行硬编码,而只需为图标添加一点空间,让客户端和子类设置自己的图标。

输入图片此处描述

在上图中,我将原始插图绘制为绿色,将附加插图绘制为红色。首先您要扩展 JTextField。我们跟踪两件事:原始插图(绿色的)mBorder 和图标。

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

    // ...
}

然后你需要重写 setBorder() 方法。

@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);
    }
}

在这里,如果我们有一个图标(字段 mIcon 不是 null),我们使用复合边框添加额外的插图。然后,您还应该覆盖 paintComponent() 方法。

@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);
    }
}

最后,您需要一个 setIcon() 方法。

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

private void resetBorder() {
    setBorder(mBorder);
}

我们在这里所做的是保存图标并重新计算边框。

如果您想对 JPasswordField 执行相同的操作,最优雅的方法可能是使用上面讨论的所有方法创建一个辅助类。

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);
    }
}

并像这样使用它:

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());
    }
}

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.

enter image description here

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());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文