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

时间:2015-07-19 20:24:01

标签: java swing events event-handling custom-component

我创建了一个图形组件,允许您查看图像并允许您选择图像的一部分:通过在此图像上绘制一个矩形来完成图像的一部分的选择(使用拖动 - 和下降)。

为此,我使用了this example,它创建了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是来自this example的班级SelectionArea

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

我读了create a custom event in Java的方法。此外,this article标识了两种用于创建活动的超级类型:EventObjectAWTEvent。本文指出:

  

通常,您可以为图形生成的事件扩展AWTEvent   任何时候组件和EventObject。

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

public class ImageSelectionEvent extends AWTEvent
{

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

}

文档标识id as the event type。那么,应该为此参数分配什么值?

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

  

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

这个怎么样?

2 个答案:

答案 0 :(得分:2)

我不知道创建自定义活动需要什么。

但是,由于您正在扩展JLabel,您可以创建一个PropertyChangeEvent

要生成事件,您只需使用以下内容:

firePropertyChange("selectionRectangle", oldRectangle, newRectangle);

然后你可以使用PropertyChangeListener来监听&#34; selectionRectangle&#34;变化。

答案 1 :(得分:1)

Javadoc for AWTEvent说:

  

在java.awt.event包之外定义的此根AWTEvent类的子类应定义大于RESERVED_ID_MAX定义的值的事件ID值。

This value is 1999。您可以将其设置为您想要的高于此值的任何值。此值由所有不同类型的Swing事件指定,Swing使用小于该值的值。例如,MouseEvent事件类型使用500-507中的值。

主要是为您的活动使用一致的值。

最后,我会考虑将ComponentEvent细分为AWTEvent,因为您的活动来源是Component,而不是Object