java 用图像和提示装饰 JTextField

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6089410/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 14:16:20  来源:igfitidea点击:

Decorating a JTextField with an image and hint

javaswinguser-interfacedecoratorjtextfield

提问by Aidan

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.

我正在尝试使用图像和提示创建一些更好看的 JTextField。为此,我制作了一个装饰器来覆盖paintComponent 方法。我使用装饰器的原因是我想将它应用于其他类型的 JTextField,例如 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.

在左边的表格中看到的问题是,即使我使用了 JPasswordField,paintComponent 似乎忽略了我假设的密码paintComponent,它可能是密码屏蔽符号。

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.

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

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.

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

回答by camickr

Text Promptworks with a JPasswordField.

文本提示与 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.

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

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

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

Edit:

编辑:

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

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

回答by mneri

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.

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

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

    // ...
}

Then you need to override setBorder()method.

然后你需要覆盖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);
    }
}

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

在这里,如果我们有一个图标(该字段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);
    }
}

Finally, you need a setIcon()method.

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

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.

如果你想用 做同样的事情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);
    }
}

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