【问题标题】:Painting issue with JLayer and JPanelJLayer 和 JPanel 的绘画问题
【发布时间】:2014-08-03 18:06:42
【问题描述】:

当用户输入无效时,我想绘制一个图标。我找到了 Oracle 的一个示例,并出于我的目的对其进行了修改。 Icon 的绘制工作正常,但是当我更改值以更正图标时,该图标并非完全不可见:仍然显示在 JPanel 上绘制的部分。

这是我的代码:

import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.text.NumberFormat;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.plaf.LayerUI;

public class FieldValidator extends JPanel {
    private static final int ICON_SIZE = 12;
    private static final Icon ICON = createResizedIcon((ImageIcon) UIManager.getIcon("OptionPane.errorIcon"));
    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createUI();
            }
        });
    }

    public static void createUI() {
        final JFrame f = new JFrame ("FieldValidator");

        final JComponent content = createContent();

        f.add (content);

        f.pack();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocationRelativeTo (null);
        f.setVisible (true);
    }

    private static JComponent createContent() {
        final LayerUI<JPanel> panelUI = new ValidationLayerUI();

        // Number field.
        final JLabel numberLabel = new JLabel("Number:");

        final NumberFormat numberFormat = NumberFormat.getInstance();
        final JFormattedTextField numberField = new JFormattedTextField(numberFormat) {
            /**
             * {@inheritDoc}
             */
            @Override
            public void replaceSelection(String content) {
                super.replaceSelection(content);
                getParent().repaint();
            }
        };
        numberField.setColumns(16);
        numberField.setFocusLostBehavior(JFormattedTextField.PERSIST);
        numberField.setValue(42);

        final int i = (ICON_SIZE / 2) + (ICON_SIZE % 2);
        final JPanel numberPanel = new JPanel();
        numberPanel.add(numberLabel);
        final JPanel panel = new JPanel(new GridBagLayout());
        final GridBagConstraints constr = new GridBagConstraints();
        constr.insets = new Insets(i, i, i, i);
        constr.weightx = 1;
        constr.weighty = 1;
        constr.fill = GridBagConstraints.BOTH;
        panel.add(numberField, constr);
        numberPanel.add(new JLayer<JPanel>(panel, panelUI));

        return numberPanel;
    }

    //Icon resized to 12x12
    private static Icon createResizedIcon(ImageIcon anIcon) {
        final BufferedImage result = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g = result.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        g.drawImage(anIcon.getImage(), 0, 0, ICON_SIZE, ICON_SIZE, null);
        g.dispose();
        return new ImageIcon(result);

    }
    static class ValidationLayerUI extends LayerUI<JPanel> {

        @Override
        public void paint (Graphics g, JComponent c) {
            super.paint (g, c);
            final JLayer jlayer = (JLayer) c;
            final JPanel panel = (JPanel) jlayer.getView();
            final JFormattedTextField ftf = (JFormattedTextField) panel.getComponent(0);
            if (!ftf.isEditValid()) {
                ICON.paintIcon(panel, g, 0, panel.getHeight() - ICON.getIconHeight());
            }
        }

    }
}

这是屏幕: 初始都是正确的

当我绘制无效图标时,一切仍然正确

但是当值正确时,只有文本字段会被重新绘制

如何强制 JPanel 重新绘制???

附:我已经找到了一种正确的 JLayeredPane 方法,但我想知道我的代码有什么问题?

【问题讨论】:

    标签: java swing jlayer


    【解决方案1】:

    如何使用DocumentListener

    numberField.getDocument().addDocumentListener(new DocumentListener() {
      @Override public void insertUpdate(DocumentEvent e) {
        //Container c = numberField.getParent();
        Container c = SwingUtilities.getUnwrappedParent(numberField);
        if (c != null) {
          c.repaint();
        }
      }
      @Override public void removeUpdate(DocumentEvent e) {
        insertUpdate(e);
      }
      @Override public void changedUpdate(DocumentEvent e) {}
    });
    

    编辑

    来自此链接的引用:Painting in AWT and Swing

    RepaintManager Swing 的 RepaintManager 类的目的是 最大化 Swing 容器上重绘处理的效率 层次结构,以及实现 Swing 的“重新验证”机制( 后者将是另一篇文章的主题)。它实现了 通过拦截 Swing 上的所有重绘请求来重绘机制 组件(因此它们不再由 AWT 处理)和 在需要更新的内容上保持自己的状态(称为“脏 region")。最后,它使用 invokeLater() 来处理待处理的 事件分派线程上的请求,如 部分所述 关于“重绘处理”(选项 B)。

    在这种情况下,当isEditValid() 状态发生变化时,父 JPanel 不是脏区域。所以保留以前的Icon 油漆。

    【讨论】:

    • @aterai 请你为什么在关于 JLayer 的代码示例中调用 updateUI/repaint(),真的不可能通知容器(画家)关于绘画的变化
    • 感谢您的解决方案。问题是:为什么它适用于文档侦听器但不适用于 replaceSelection???
    • @SergiyMedvynskyy JTextComponent#replaceSelection(...) is not called 部分文本已被删除。
    • @aterai 很奇怪,但你是对的。我确信它也用空字符串调用了删除。最大过失:)
    猜你喜欢
    • 2013-06-22
    • 2011-11-05
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    相关资源
    最近更新 更多