Украшение JTextField с изображением и подсказкой - PullRequest
9 голосов
/ 22 мая 2011

Я пытаюсь создать более привлекательный вид JTextFields с изображением и подсказкой.Для этого я сделал декоратор, который переопределяет метод paintComponent.Причина, по которой я использовал декоратор, заключается в том, что я хотел применить его к другим типам JTextField, таким как JPasswordField.

Вот что я сделал до сих пор;

enter image description here

Проблема, как видно из формы слева, заключается в том, что, хотя я использовал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,но я не вижу, как решить эту проблему, не дублируя код.

Ответы [ 2 ]

8 голосов
/ 22 мая 2011

Текстовая подсказка работает с JPasswordField.

Одно отличие состоит в том, что отображаемый значок исчезает при вводе текста. Если вы хотите, чтобы значок был постоянным, тогда я предлагаю вам создать собственный класс "IconBorder *, чтобы рисовать значок, а не делать пользовательское рисование в методе paintComponent ().

Ваш подход не будет работать, если вы не продублируете код для JTextField и JPasswordField.

Edit:

На самом деле вам не нужно создавать собственный IconBorder. MatteBorder поддерживает рисование иконки на границе.

4 голосов
/ 05 ноября 2016

Чтобы нарисовать иконку внутри текстового поля, нужно добавить несколько вставок. Вы не хотите жестко кодировать вставки в своем компоненте, а просто добавьте немного места для значка, позволяя клиентам и подклассам устанавливать свои собственные.

enter image description here

На рисунке выше я нарисовал оригинальные вставки зеленым и дополнительные вставки красным Первое, что вы хотите расширить 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());
    }
}
...