如何在绘制新矩形时触发自定义事件



我创建了一个图形组件,它允许您查看图像并允许您对图像的一部分进行选择:通过在该图像上绘制矩形(使用拖放)来完成图像的一部分的选择。

为此,我使用了这个例子,它创建了JLabel的一个子类来绘制图像,并处理矩形的绘制。然后,我将这个子类的实例放在JPanel中,以便使图像始终位于面板的中心。

FigurePanel.java

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;

public class FigurePanel extends JPanel
{
    private SelectionLabel imageLabel = null;

    public FigurePanel()
    {
        this.setLayout(new GridBagLayout());
        imageLabel = new SelectionLabel();
        this.add(imageLabel, null);
    }
    public void setImage(Image image)
    {
        imageLabel.setImage(image);
    }
    private class SelectionLabel extends JLabel
    {
        private Rectangle currentRect = null;
        private Rectangle rectToDraw = null;
        private final Rectangle previousRectDrawn = new Rectangle();

        public SelectionLabel()
        {
            super();
            setOpaque(true);
            SelectionListener listener = new SelectionListener();
            addMouseListener(listener);
            addMouseMotionListener(listener);
        }

        public void setImage(Image image)
        {
            currentRect = null;
            rectToDraw = null;
            previousRectDrawn.setBounds(0, 0, 0, 0);
            setIcon(new ImageIcon(image));
        }
        private class SelectionListener extends MouseInputAdapter
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                int x = e.getX();
                int y = e.getY();
                currentRect = new Rectangle(x, y, 0, 0);
                updateDrawableRect(getWidth(), getHeight());
                repaint();
            }
            @Override
            public void mouseDragged(MouseEvent e)
            {
                updateSize(e);
            }
            @Override
            public void mouseReleased(MouseEvent e)
            {
                updateSize(e);
            }
            /* 
             * Update the size of the current rectangle
             * and call repaint.  Because currentRect
             * always has the same origin, translate it
             * if the width or height is negative.
             * 
             * For efficiency (though
             * that isn't an issue for this program),
             * specify the painting region using arguments
             * to the repaint() call.
             * 
             */
            void updateSize(MouseEvent e)
            {
                int x = e.getX();
                int y = e.getY();
                currentRect.setSize(x - currentRect.x,
                                    y - currentRect.y);
                updateDrawableRect(getWidth(), getHeight());
                Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
                repaint(totalRepaint.x, totalRepaint.y,
                        totalRepaint.width, totalRepaint.height);
            }
        }
        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g); //paints the background and image
            //If currentRect exists, paint a box on top.
            if (currentRect != null) {
                //Draw a rectangle on top of the image.
                g.setXORMode(Color.white); //Color of line varies
                                           //depending on image colors
                g.drawRect(rectToDraw.x, rectToDraw.y, 
                           rectToDraw.width - 1, rectToDraw.height - 1);
                System.out.println("rectToDraw: " + rectToDraw);
            }
        }
        private void updateDrawableRect(int compWidth, int compHeight)
        {
            int x = currentRect.x;
            int y = currentRect.y;
            int width = currentRect.width;
            int height = currentRect.height;
            //Make the width and height positive, if necessary.
            if (width < 0) {
                width = 0 - width;
                x = x - width + 1; 
                if (x < 0) {
                    width += x; 
                    x = 0;
                }
            }
            if (height < 0) {
                height = 0 - height;
                y = y - height + 1; 
                if (y < 0) {
                    height += y; 
                    y = 0;
                }
            }
            //The rectangle shouldn't extend past the drawing area.
            if ((x + width) > compWidth) {
                width = compWidth - x;
            }
            if ((y + height) > compHeight) {
                height = compHeight - y;
            }
            //Update rectToDraw after saving old value.
            if (rectToDraw != null) {
                previousRectDrawn.setBounds(
                            rectToDraw.x, rectToDraw.y, 
                            rectToDraw.width, rectToDraw.height);
                rectToDraw.setBounds(x, y, width, height);
            } else {
                rectToDraw = new Rectangle(x, y, width, height);
            }
        }
    }
}

FigurePanelTest.java

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JScrollPane;

public class FigurePanelTest extends JFrame
{
    public FigurePanelTest()
    {
        FigurePanel imagePanel = new FigurePanel();
        JScrollPane imageScrollPane = new JScrollPane();
        imageScrollPane.setPreferredSize(new Dimension(420, 250));
        imageScrollPane.setViewportView(imagePanel);
        JButton imageButton = new JButton("Load Image");
        imageButton.addActionListener(
                new ActionListener()
                {
                    @Override
                    public void actionPerformed(ActionEvent evt)
                    {
                        JFileChooser fc = new JFileChooser();
                        int returnValue = fc.showOpenDialog(null);
                        if (returnValue == JFileChooser.APPROVE_OPTION) {
                            File selectedFile = fc.getSelectedFile();
                            System.out.println(selectedFile.getName());
                            try
                            {
                                Image image = ImageIO.read(selectedFile.getAbsoluteFile());
                                imagePanel.setImage(image);
                                imageScrollPane.getViewport().setViewPosition(new Point(0, 0));
                            }
                            catch(IOException e)
                            {
                                e.printStackTrace();
                            }
                        }
                    }
                }
        );
        Container container = getContentPane();
        container.setLayout(new BorderLayout());
        container.add(imageScrollPane, BorderLayout.CENTER);
        container.add(imageButton, BorderLayout.NORTH);
        setSize(600, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new FigurePanelTest().setVisible(true);
            }
        });
    }
}

私有类SelectionLabel就是本例中的SelectionArea类。

当绘制一个新的矩形时,将在控制台上打印一条消息。现在我将用一个自定义事件的触发来替换消息的打印,这样应用程序业务逻辑就可以访问矩形的位置和大小。

我读了如何在Java中创建自定义事件。此外,本文还确定了用于创建事件的两个超类型:EventObject和AWTEvent。这篇文章指出:

通常您扩展AWTEvent来处理图形生成的事件组件和EventObject,

由于关于选择图像一部分的事件是由图形组件(即FigurePanel面板)生成的,因此我可以通过扩展AWTEvent来实现ImageSelectionEvent类,如下面的代码片段所示。

public class ImageSelectionEvent extends AWTEvent
{
    public ImageSelectionEvent(Object source, int id) {
        super(source, id);
    }
}

文档将id标识为事件类型。那么,应该给这个参数赋什么值呢?

此外,为什么EventObject类的构造函数没有id参数?

创建事件类时,必须保证事件是不可变的。事件生成器将共享同一个事件实例在听众中;确保任何一个监听器都不能更改事件的状态。

这个怎么样?

我不知道创建自定义事件需要什么。

然而,因为你正在扩展JLabel,也许你可以只创建一个PropertyChangeEvent .

要生成事件,您只需使用如下命令:

firePropertyChange("selectionRectangle", oldRectangle, newRectangle);

然后你可以使用PropertyChangeListener来监听"selectionRectangle"的变化

AWTEvent的Javadoc说:

在java.awt.event包之外定义的这个根awteevent类的子类定义的事件ID值应该大于RESERVED_ID_MAX定义的值。

该值为1999。你可以把它设为任何你想要的比它高的值。该值由所有不同类型的Swing事件指定,并且Swing使用小于该值的值。例如,MouseEvent事件类型使用500-507的值。

最重要的是要为你的事件使用一致的值。

最后,我会考虑将ComponentEvent子类化到AWTEvent之上,因为您的事件源是Component,而不是Object

相关内容

  • 没有找到相关文章

最新更新