简体   繁体   English

JLayer和JPanel的绘画问题

[英]Painting issue with JLayer and JPanel

I want to paint an icon when user's input is invalid. 我想在用户输入无效时绘制图标。 I've found an example by Oracle and modified it for my purposes. 我找到了Oracle的一个示例,并出于我的目的对其进行了修改。 The painting of Icon works correctly but when I change the value to correct the icon goes not completly invisible: the part which is drawn over the JPanel is still displayed. Icon的绘画工作正常,但是当我更改值以更正图标时,它不会完全不可见:在JPanel上绘制的部分仍会显示。

Here is my code: 这是我的代码:

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

    }
}

Here is the screens: Initial all is correct 这是屏幕:初始全部正确

在此处输入图片说明

When I paint invalid Icon all is still correct 当我绘制无效的图标时,一切仍然正确

在此处输入图片说明

But when the value goes correct only the text field will be repainted 但是,当值正确时,将仅重绘文本字段

在此处输入图片说明

How can I force the JPanel to repaint??? 如何强制JPanel重新绘制???

PS I've already found an approach with JLayeredPane which works correct, but I want to know what is wrong in my code? PS我已经找到了使用JLayeredPane的方法,该方法可以正常工作,但是我想知道代码中的错误是什么?

How about using the DocumentListener : 如何使用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) {}
});

Edit 编辑

Quote from this link: Painting in AWT and Swing 来自这个链接的报价: AWT和Swing中的绘画

The RepaintManager The purpose of Swing's RepaintManager class is to maximize the efficiency of repaint processing on a Swing containment hierarchy, and also to implement Swing's 'revalidation' mechanism (the latter will be a subject for a separate article). RepaintManager Swing的RepaintManager类的目的是在Swing包含层次结构中最大化重画处理的效率 ,并实现Swing的“重新验证”机制(后者将成为另一篇文章的主题)。 It implements the repaint mechanism by intercepting all repaint requests on Swing components (so they are no longer processed by the AWT) and maintaining its own state on what needs to be updated (known as "dirty regions"). 它通过拦截Swing组件上的所有重绘请求(因此它们不再由AWT处理)并在需要更新的内容上保持其自己的状态(称为“脏区”)来实现重绘机制。 Finally, it uses invokeLater() to process the pending requests on the event dispatching thread, as described in the section on "Repaint Processing" (option B). 最后,如“重画处理”(选项B)部分所述,它使用invokeLater()处理事件分配线程上的待处理请求。

In this case the parent JPanel is not dirty region when isEditValid() status changed. 在这种情况下,当isEditValid()状态更改时,父JPanel不会是脏区域。 so remaining previous Icon paint. 因此保留以前的Icon颜料。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM