java - 直接在玻璃板上绘图 : not working

标签 java swing glasspane

我正在使用 Swing 开发一个应用程序。当用户与应用程序交互时,帮助数据可能会在屏幕的封闭部分中变得可用,我想在发生这种情况时绘制一个指向该部分的小箭头。

为此,我扩展了 JPanel 并将其添加为应用程序 jframe 的玻璃 Pane 。自定义玻璃 Pane 类的名称是 AlertGlassPane。

AlertGlassPane 执行此操作:WAITING新的帮助数据可用。当发生这种情况并且帮助部分关闭时,它会找到帮助部分在屏幕上的位置,然后在其侧面绘制一个动画箭头。

为了绘制箭头,我扩展了玻璃 Pane 的 PaintComponent 方法。

为了对箭头进行动画处理,我创建了一个每 100 毫秒循环一次的小线程,在玻璃 Pane 上调用重绘。

问题:Java 忽略我的绘图...如果触发动画开始并静止不动,则不会发生任何情况。应用程序上不显示任何绘图。

我知道循环正在运行并且paintComponent正在被调用,但是自定义绘制没有生效。

但是如果我将鼠标移到应渲染箭头的区域上,它就会渲染!但就在鼠标移动的时候。如果停止移动鼠标,动画也会停止,并且箭头会卡住在鼠标停止(或离开该区域)之前的最后一个位置。

我尝试了很多东西(例如设置绘图的剪辑区域),但似乎没有任何效果。唯一有效的是当我从 PaintComponent 内部错误地调用 repaint 时。

此刻,我正在寻找一种与渲染系统通信的方法,告知我的玻璃板的给定区域需要重新绘制(重绘(x,y,w,h)不起作用...)。也许将自定义渲染移至 JLabel,然后将此标签添加到 glasspane?我不喜欢这种方法...

在此处发布代码片段之前,我会尝试清理代码。有帮助吗?

提前致谢!

片段:

package br.com.r4j.test;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;

/**
 *
 * @author 
 */
public class TestGlassPaneAnimation extends JPanel
{
    private static TestGlassPaneAnimation gvp = new TestGlassPaneAnimation();



    public static void main(String args[])
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                JFrame f = new JFrame("Anitest in glass");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //f.setResizable(false);
                f.setLayout(new GridLayout(5, 3));

                f.add(new JLabel("First Name :"));
                f.add(new JTextField(20));
                f.add(new JLabel("Last Name :"));
                f.add(new JTextField(20));
                f.add(new JLabel("Phone Number :"));
                f.add(new JTextField(20));
                f.add(new JLabel("Email:"));
                f.add(new JTextField(20));
                f.add(new JLabel("Address :"));
                f.add(new JTextField(20));
                JButton btnStart = new JButton("Click me, please!");
                f.add(btnStart);

                btnStart.addActionListener(new ActionListener()
                {
                    public void actionPerformed(ActionEvent e)
                    {
                        gvp.startAnimation();
                    }
                });

                f.setGlassPane(gvp);

                f.pack();
                f.setVisible(true);
                gvp.setVisible(true);
            }

        });
    }

    private BufferedImage icon;
    private boolean animate = false;
    private long timeStart = 0;
    private Thread thrLastActive = null;

    public TestGlassPaneAnimation()
    {
        setLayout(null);//this is the exception to the rule case a layoutmanager might make setting Jlabel co-ords harder
        setOpaque(false);
        Icon icon1 = UIManager.getIcon("OptionPane.warningIcon");
        int imgW = icon1.getIconWidth();
        int imgH = icon1.getIconHeight();
        this.icon = ImageUtilities.getBufferedImageOfIcon(icon1, imgW, imgH);
        this.animate = false;
    }


    public void startAnimation()
    {
        this.animate = true;
        this.timeStart = (new Date()).getTime();

        if (this.thrLastActive != null)
            this.thrLastActive.interrupt();

        this.thrLastActive = new Thread(new Runnable() 
        {
            public void run()
            {
                try
                {
                    while (true)
                    {
                        // int x = 250, y = 250;
                        // int width = 60, height = 60;

                        Thread.currentThread().sleep(100);

                        // frmRoot.invalidate();
                        // repaint(x, y, width, height);
                        repaint(new Rectangle(x, y, width, height));
                        // repaint(new Rectangle(10, 10, 2000, 2000));
                        // repaint();
                        // paintComponent(getGraphics());
                    }
                }
                catch (Exception e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });

        this.thrLastActive.start();
    }


    protected void paintComponent(Graphics g)
    {
      try
      {
            // enables anti-aliasing
            Graphics2D g2 = (Graphics2D) g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);


            java.awt.Composite composite = g2.getComposite();

            System.err.println("b1: " + g.getClipBounds());

            if (this.animate)
            {
                long timeSpent = (new Date()).getTime() - timeStart;

                int x = 10, y = 150;
                int width = 60, height = 60;
                float maxAlpha = 0.8f;
                x += (-100*Math.sin(5*2*Math.PI*timeSpent/10000)+50)/15;
                System.err.println("painting::x: " + x + ", y: " + y + ", sin: " + (Math.sin(6*2*Math.PI*timeSpent/10000)));

                // g.setClip(x-10, y-10, width, height);
                System.err.println("b2: " + g.getClipBounds());

                AlphaComposite alpha2 = AlphaComposite.SrcOver.derive(maxAlpha);
                g2.setComposite(alpha2);
                g2.drawImage(this.icon, x, y, null);
                g2.setComposite(composite);

                g2.setComposite(composite);
            }
      }
      catch (Throwable e)
      {
          System.err.println("Errr!");
          e.printStackTrace();
      }
    }

}

class ImageUtilities {

    public static BufferedImage resize(BufferedImage image, int width, int height) {
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
        Graphics2D g2d = (Graphics2D) bi.createGraphics();
        g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
        g2d.drawImage(image, 0, 0, width, height, null);
        g2d.dispose();
        return bi;
    }

    public static BufferedImage getBufferedImageOfIcon(Icon icon, int imgW, int imgH) {
        BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D) img.getGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        icon.paintIcon(null, g2d, 0, 0);
        g2d.dispose();
        return img;
    }
}

最佳答案

嗯,不确定您提供的代码片段究竟发生了什么,但因为它看起来像是从我的代码片段中取出的,所以我编写了另一个示例(我必须更改 showWarningIcon(Component c) 中的 1 或 2 行代码refreshLocations() 方法,但没什么大不了的:

如果输入了除 david 之外的任何内容,并且单击了按钮(请点击我),则会显示以下内容:

enter image description here

import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class TestGlassPaneAnimation {

    private static GlassValidationPane gvp = new GlassValidationPane();

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame("Anitest in glass");
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                //f.setResizable(false);
                f.setLayout(new GridBagLayout());

                GridBagConstraints gc = new GridBagConstraints();
                gc.fill = GridBagConstraints.HORIZONTAL;
                gc.weightx = 1;
                gc.weighty = 1;
                gc.insets = new Insets(15, 15, 15, 15);//give some space so icon doesnt cover components when shown

                gc.gridx = 0;
                gc.gridy = 0;
                f.add(new JLabel("First Name:"), gc);

                final JTextField jtf = new JTextField(20);
                gc.gridx = 1;
                f.add(jtf, gc);

                gc.gridx = 0;
                gc.gridy = 1;
                f.add(new JLabel("Surname:"), gc);

                final JTextField jtf2 = new JTextField(20);
                gc.gridx = 1;
                f.add(jtf2, gc);

                JButton btnStart = new JButton("Click me, please!");
                gc.gridx = 2;
                f.add(btnStart, gc);

                btnStart.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        if (!jtf.getText().equalsIgnoreCase("david")) {
                            gvp.showWarningIcon(jtf);
                        }
                    }
                });

                f.addComponentListener(new ComponentAdapter() {//so wjen frame is resized icons follow
                    @Override
                    public void componentResized(ComponentEvent ce) {
                        super.componentResized(ce);
                        gvp.refreshLocations();
                    }
                });
                f.setGlassPane(gvp);

                f.pack();
                f.setVisible(true);
                gvp.setVisible(true);
            }
        });
    }
}

class GlassValidationPane extends JPanel {

    private HashMap<Component, JLabel> warningLabels = new HashMap<>();
    private ImageIcon warningIcon;

    public GlassValidationPane() {
        setLayout(null);//this is the exception to the rule case a layoutmanager might make setting Jlabel co-ords harder
        setOpaque(false);
        Icon icon = UIManager.getIcon("OptionPane.warningIcon");
        int imgW = icon.getIconWidth();
        int imgH = icon.getIconHeight();
        BufferedImage img = ImageUtilities.getBufferedImageOfIcon(icon, imgW, imgH);
        warningIcon = new ImageIcon(ImageUtilities.resize(img, 24, 24));
    }

    void showWarningIcon(Component c) {
        if (warningLabels.containsKey(c)) {
            return;
        }

        JLabel label = new JLabel();
        label.setIcon(warningIcon);

        //int x=c.getX();//this will make it insode the component
        int x = c.getX() - warningIcon.getIconWidth();//this makes it appear outside/next to component if space
        int y = c.getY();

        label.setBounds(x, y, warningIcon.getIconWidth(), warningIcon.getIconHeight());
        add(label);
        revalidate();
        repaint();
        warningLabels.put(c, label);
    }

    public void removeWarningIcon(Component c) {
        for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
            Component component = entry.getKey();
            JLabel jLabel = entry.getValue();
            if (component == c) {
                remove(jLabel);
                revalidate();
                repaint();
                break;
            }
        }
        warningLabels.remove(c);
    }

    public void refreshLocations() {
        for (Map.Entry<Component, JLabel> entry : warningLabels.entrySet()) {
            Component c = entry.getKey();
            JLabel label = entry.getValue();
            //int x=c.getX();//this will make it insode the component
            int x = c.getX() - label.getIcon().getIconWidth();//this makes it appear outside/next to component
            int y = c.getY();

            label.setBounds(x, y, label.getIcon().getIconWidth(), label.getIcon().getIconHeight());
            revalidate();
            repaint();
        }
    }
}

class ImageUtilities {

    public static BufferedImage resize(BufferedImage image, int width, int height) {
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
        Graphics2D g2d = (Graphics2D) bi.createGraphics();
        g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
        g2d.drawImage(image, 0, 0, width, height, null);
        g2d.dispose();
        return bi;
    }

    public static BufferedImage getBufferedImageOfIcon(Icon icon, int imgW, int imgH) {
        BufferedImage img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = (Graphics2D) img.getGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        icon.paintIcon(null, g2d, 0, 0);
        g2d.dispose();
        return img;
    }
}

如果您正在寻找更成熟的库,请查看 JXLayer - Validation OverlaysValidation overlays using glass pane .

也可能想读一读here其中显示了验证文本字段数据的多种方法(除了按下按钮之外),例如 DocumentFilterInputVerifier 等。

关于java - 直接在玻璃板上绘图 : not working,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14896509/

相关文章:

java - 如何在 Java 中以 root 权限运行 shell 命令

swing - 如果我开始使用 Netbeans GUI 构建器制作 swing 应用程序可以吗?

java - 如何垂直移动 JApplet 中的图像?

java - 如何正确格式化 ActionEvent 以便 JButton 正常工作

java - 在玻璃板上绘制图形

Java特殊数组补顶/覆盖

java - 为什么这个 map 检索会产生 NPE?

java - 如何将 JGraph 转换为玻璃板?

java - 如何在Java中使用未知的KEY和IV解密AES?

java - 在线程进行额外处理之前和之后重新绘制玻璃板