我正在尝试用图像和提示创建一些看起来更漂亮的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,但我看不出如何在不重复代码的情况下解决这个问题。答案 0 :(得分:8)
Text Prompt适用于JPasswordField。
一个区别是输入文本时显示的图标会消失。如果你想让图标永久化,那么我建议你创建一个自定义的“IconBorder *类来绘制一个Icon,然后在paintComponent()方法中进行自定义绘制。
除非您复制JTextField和JPasswordField的代码,否则您的方法将无效。
编辑:
实际上您不需要创建自定义IconBorder。 MatteBorder支持在边框中绘制图标。
答案 1 :(得分:4)
要在文本字段中绘制图标,您需要添加一些插图。 您不希望在组件中硬编码插件,只需为图标添加一点空间,让客户端和子类自行设置。
在上图中,我用绿色绘制原始插图,用红色绘制其他插图。首先要扩展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 compoud = BorderFactory.createCompoundBorder(border, margin);
super.setBorder(compoud);
}
}
在这里,如果我们有一个图标(字段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());
}
}