0

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

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

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

为了绘制箭头,我扩展了玻璃窗格的 paintComponent 方法。

为了使箭头动画化,我创建了一个每 100 毫秒循环一次的小线程,在玻璃窗格上调用 repaint。

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

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

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

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

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

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

提前致谢!

片段:

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;
    }
}
4

1 回答 1

1

嗯,不确定你给出的代码片段到底是怎么回事,但看起来它是从我的片段中获取的,所以我写了另一个例子(我不得不在showWarningIcon(Component c)refreshLocations()方法中更改 1 或 2 行代码,但没什么大不了的:

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

在此处输入图像描述

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

也可能想在这里阅读它显示了许多验证文本字段数据(除了按钮按下)之类的方法DocumentFilterInputVerifier

于 2013-02-15T20:59:41.290 回答