JTextFieldUI 绘制在 JTabbedPane 的另一个选项卡中,具有我个人的外观和感觉

JTextFieldUI painted in another tab of the JTabbedPane with my personal look and feel

我正在开发一种新的外观和感觉,现在我在 JTabbledPane 组件中使用组件 JtextField 时遇到了一个错误。

所以错误是这样的

我的 JTextFieldUI 有这段代码,它扩展了 BasicLookAndFell

public class MyPersonalFieldUI extends BasicTextFieldUI {

    protected static final String PROPERTY_LINE_COLOR = "lineColor";
    protected static final String PROPERTY_SELECTION_COLOR = "selectionColor";
    protected static final String PROPERTY_SELECTION_TEXT_COLOR = "selectedTextColor";
    protected static final String ProprietyPrefix = "TextField";

    protected boolean drawLine;
    protected JTextComponent textComponent;
    protected Color background;
    protected Color foreground;
    protected Color activeBackground;
    protected Color activeForeground;
    protected Color inactiveBackground;
    protected Color inactiveForeground;
    protected Color colorLineInactive;
    protected Color colorLineActive;
    protected Color colorLine;
    protected FocusListener focusListenerColorLine;
    protected PropertyChangeListener propertyChangeListener;
    protected PropertyChangeSupport propertyChangeSupport;

    public MyPersonalFieldUI() {
        this(true);
    }

    public MyPersonalFieldUI(boolean drawLine) {
        super();
        this.drawLine = drawLine;
        this.focusListenerColorLine = new FocusListenerColorLine();
        this.propertyChangeListener = new MaterialPropertyChangeListener();
        this.propertyChangeSupport = new PropertyChangeSupport(this);
    }

    @Override
    protected String getPropertyPrefix() {
        return ProprietyPrefix;
    }

    public static ComponentUI createUI(JComponent c) {
        return new MyPersonalFieldUI();
    }

    @Override
    public void installUI(JComponent c) {
        super.installUI(c);
        this.textComponent = (JTextField) c;
    }

    @Override
    protected void installDefaults() {
        super.installDefaults();
        installMyDefaults();
    }

    @Override
    public void uninstallUI(JComponent c) {
        super.uninstallUI(c);

        c.setFont(null);
        c.setBackground(null);
        c.setForeground(null);
        c.setBorder(null);
        c.setCursor(null);

    }

    @Override
    protected void uninstallDefaults() {
        super.uninstallDefaults();
        getComponent().setBorder(null);
    }

    @Override
    protected void installListeners() {
        super.installListeners();
        getComponent().addFocusListener(focusListenerColorLine);
        getComponent().addPropertyChangeListener(propertyChangeListener);
        propertyChangeSupport.addPropertyChangeListener(propertyChangeListener);
    }

    @Override
    protected void uninstallListeners() {
        getComponent().removeFocusListener(focusListenerColorLine);
        getComponent().removePropertyChangeListener(propertyChangeListener);
        propertyChangeSupport.removePropertyChangeListener(propertyChangeListener);
        super.uninstallListeners();
    }

    @Override
    public void paintSafely(Graphics g) {
        super.paintSafely(g);

        paintLine(g);
    }

    protected void logicForChangeColorOnFocus(JComponent component, Color background, Color foreground) {
        if (background == null || foreground == null) {
            return;
        }
        JTextField textField = (JTextField) component;
        if (!this.activeForeground.equals(foreground)) { //TODO this comment resolve the issue but I don't not if it introduce some bug
            textField.setSelectedTextColor(inactiveForeground);
        } else {
            textField.setSelectedTextColor(foreground);
        }
        textField.setSelectionColor(background);
    }

    protected void installMyDefaults() {
        this.background = UIManager.getColor(getPropertyPrefix() + ".background");
        this.foreground = UIManager.getColor(getPropertyPrefix() + ".foreground");
        this.activeBackground = UIManager.getColor(getPropertyPrefix() + ".selectionBackground");
        this.activeForeground = UIManager.getColor(getPropertyPrefix() + ".selectionForeground");
        this.inactiveBackground = UIManager.getColor(getPropertyPrefix() + ".inactiveBackground");
        this.inactiveForeground = UIManager.getColor(getPropertyPrefix() + ".inactiveForeground");
        colorLineInactive = UIManager.getColor(getPropertyPrefix() + "[Line].inactiveColor");
        colorLineActive = UIManager.getColor(getPropertyPrefix() + "[Line].activeColor");
        getComponent().setFont(UIManager.getFont(getPropertyPrefix() + ".font"));
        colorLine = getComponent().hasFocus() && getComponent().isEditable() ? colorLineActive : colorLineInactive;
        getComponent().setSelectionColor(getComponent().hasFocus() && getComponent().isEnabled() ? activeBackground : inactiveBackground);
        getComponent().setSelectedTextColor(getComponent().hasFocus() && getComponent().isEnabled() ? activeForeground : inactiveForeground);
        getComponent().setForeground(getComponent().hasFocus() && getComponent().isEnabled() ? activeForeground : inactiveForeground);
        getComponent().setBorder(UIManager.getBorder(getPropertyPrefix() + ".border"));
    }

    protected void logicForPropertyChange(Color newColor, boolean isForeground) {
        if (newColor == null) {
            return;
        }
        if (isForeground && (!newColor.equals(activeForeground) && !newColor.equals(inactiveForeground))) {
            this.activeForeground = newColor;
            getComponent().repaint();
        }
        if (!isForeground && !newColor.equals(activeBackground) && !newColor.equals(inactiveBackground)) {
            this.activeBackground = newColor;
            getComponent().repaint();
        }
    }

    protected void changeColorOnFocus(boolean hasFocus) {
        JTextComponent c = getComponent();
        if (c == null) {
            return;
        }
        if (hasFocus && (activeBackground != null) && (activeForeground != null)) {
            logicForChangeColorOnFocus(c, activeBackground, activeForeground);
            //TODO create a new changePropriety
            paintLine(c.getGraphics());
        }

        if (!hasFocus && (inactiveBackground != null) && (inactiveForeground != null)) {
            logicForChangeColorOnFocus(c, inactiveBackground, inactiveForeground);
            paintLine(c.getGraphics());
        }
        if (c.getGraphics() != null) {
            c.paint(c.getGraphics());
        }
    }

    protected synchronized void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        if ((propertyName == null || propertyName.isEmpty()) || oldValue == null || newValue == null) {
            throw new IllegalArgumentException("Some property null");
        }
        //TODO refectoring this code
        if (propertyChangeSupport == null || (oldValue != null && newValue != null && oldValue.equals(newValue))) {
            return;
        }
        if (propertyChangeSupport == null || oldValue == newValue) {
            return;
        }
        propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }

    protected void paintLine(Graphics graphics) {
        if (graphics == null) {
            return;
        }
        JTextComponent c = getComponent();

        if (drawLine) {
            int x = c.getInsets().left;
            int y = c.getInsets().top;
            int w = c.getWidth() - c.getInsets().left - c.getInsets().right;
            graphics.setColor(colorLine);

            graphics.fillRect(x, c.getHeight() - y, w, 1);
        }
    }

    protected class FocusListenerColorLine implements FocusListener {

        @Override
        public void focusGained(FocusEvent e) {
            firePropertyChange(PROPERTY_LINE_COLOR, colorLineInactive, colorLineActive);

            changeColorOnFocus(true);
        }

        @Override
        public void focusLost(FocusEvent e) {
            firePropertyChange(PROPERTY_LINE_COLOR, colorLineActive, colorLineInactive);
            changeColorOnFocus(false);
        }
    }

    protected class MaterialPropertyChangeListener implements PropertyChangeListener {

        @Override
        public void propertyChange(PropertyChangeEvent pce) {
            if (getComponent() == null) {
                return;
            }
            if (pce.getPropertyName().equals(PROPERTY_SELECTION_COLOR)) {
                Color newColor = (Color) pce.getNewValue();
                logicForPropertyChange(newColor, false);
            }

            if (pce.getPropertyName().equals(PROPERTY_SELECTION_TEXT_COLOR)) {
                Color newColor = (Color) pce.getNewValue();
                logicForPropertyChange(newColor, true);
            }

            if (pce.getPropertyName().equals(PROPERTY_LINE_COLOR)) {
                Color newColor = (Color) pce.getNewValue();
                colorLine = newColor;
                getComponent().repaint();
            }

            if (pce.getPropertyName().equals("background")) {
                getComponent().repaint();
            }
        }
    }

}

抱歉我的大代码,但我认为有必要在其中找到我的错误

此外,这是我最小的可重现示例

public class DemoLookAndFeel extends JFrame {

    static {
        try {
             UIManager.setLookAndFeel(new MyLookAndFeel());
        } catch (UnsupportedLookAndFeelException ex) {
        }
    }

    public void init() {
        JPanel panelOne = new JPanel();
        panelOne.add(new JTextField("write inside me and change the tab"));

        JPanel panelTwo = new JPanel();
        //panelTwo.add(new Label("Now seee the JTextField?"));

        JTabbedPane tabbedPane = new JTabbedPane();

        tabbedPane.add("One", panelOne);
        tabbedPane.add("Two", panelTwo);

        this.setContentPane(tabbedPane);

        this.setSize(800,800);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    private static class MyLookAndFeel extends BasicLookAndFeel {

        @Override
        public String getName() {
            return "my look and feel";
        }

        @Override
        public String getID() {
            return "qwerty";
        }

        @Override
        public String getDescription() {
            return "";
        }

        @Override
        public boolean isNativeLookAndFeel() {
            return false;
        }

        @Override
        public boolean isSupportedLookAndFeel() {
            return true;
        }

        @Override
        protected void initClassDefaults(UIDefaults table) {
            super.initClassDefaults(table); 
              table.put("TextFieldUI", MyPersonalFieldUI.class.getCanonicalName());
        }



        @Override
        protected void initSystemColorDefaults(UIDefaults table) {
            super.initSystemColorDefaults(table); 

            table.put("TextField.background", Color.GRAY);
            table.put("TextField.foreground", Color.BLACK);
            table.put("TextField.inactiveForeground", Color.BLACK);
            table.put("TextField.inactiveBackground", Color.GRAY);
            table.put("TextField.selectionBackground", Color.BLUE);
            table.put("TextField.selectionForeground", Color.WHITE);
            table.put("TextField[Line].inactiveColor", Color.WHITE);
            table.put("TextField[Line].activeColor", Color.BLUE);
        }

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                DemoLookAndFeel demo = new DemoLookAndFeel();
                demo.init();
            }
        });
    }

}

ps:对不起我的大代码

问题在于你如何操作绘画。在 changeColorOnFocus(由 FocusListener 调用)方法中,您有这些行:

if (c.getGraphics() != null) {
    c.paint(c.getGraphics());
}

在这里,您使用图形并在组件绘制层次结构中负责绘制的方法之外进行一种自定义绘制。我希望我能更好地解释它,但长话短说,只在将它们作为参数提供给您的方法中使用 Graphics(并调用其 super 方法)。在 UI class 中,方法是 paintSafely,在组件中是 paintComponent。它们都给你 Graphics 对象,因此它们适合自定义绘画。

因此,解决方案是在您的 FocusListener 中添加一个标志以了解组件是否获得焦点,并在 paintSafely(Graphics g) 方法中调用 changeColorOnFocus 。你的 UI class 应该在以下部分进行更改(我没有全部粘贴,因为它有点大)。

private boolean focused; //a field
protected class FocusListenerColorLine implements FocusListener {

    @Override
    public void focusGained(FocusEvent e) {
        firePropertyChange(PROPERTY_LINE_COLOR, colorLineInactive, colorLineActive);
        focused = true;
    }

    @Override
    public void focusLost(FocusEvent e) {
        firePropertyChange(PROPERTY_LINE_COLOR, colorLineActive, colorLineInactive);
        focused = false;
    }
}



@Override
public void paintSafely(Graphics g) {
    super.paintSafely(g);
    paintLine(g);
    changeColorOnFocus(g);
}

protected void changeColorOnFocus(Graphics g) {
    boolean hasFocus = focused;
    JTextComponent c = getComponent();
    if (c == null) {
        return;
    }
    if (hasFocus && (activeBackground != null) && (activeForeground != null)) {
        logicForChangeColorOnFocus(c, activeBackground, activeForeground);
        //TODO create a new changePropriety
        paintLine(c.getGraphics());
    }

    if (!hasFocus && (inactiveBackground != null) && (inactiveForeground != null)) {
        logicForChangeColorOnFocus(c, inactiveBackground, inactiveForeground);
        paintLine(c.getGraphics());
    }
}

预览: