2015-07-19 46 views
1

我创建了一个图形组件,允许您查看图像,并允许您选择图像的一部分:图像的一部分的选择是通过在该图像上绘制矩形来完成的(使用拖放和-下降)。如何绘制新的矩形时触发自定义事件?

为了达到此目的,我使用了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); 
      } 
     }); 
    } 

} 

私有类SelectionLabelthis exampleSelectionArea

当绘制一个新的矩形时,控制台上将打印一条消息。现在,我将用自定义事件的触发来替换消息的打印,以便应用程序业务逻辑可以访问矩形的位置和大小。我看过create a custom event in Java。此外,this article标识用于创建事件的两种超级类型:EventObjectAWTEvent。这篇文章指出:

通常情况下,您扩展AWTEvent的任何其他时间由图形 组件和EventObject生成的事件。

由于由一个图形组件(也就是FigurePanel面板)被产生关于图像的一部分的选择的情况下,我可以通过延伸AWTEvent,如下面的代码段实现ImageSelectionEvent类。

public class ImageSelectionEvent extends AWTEvent 
{ 

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

} 

该文件标识id as the event type。那么,应该给这个参数分配什么值?

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

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

这是怎么回事?

+1

AWTEvent和EventObject之间有一个普遍的区别,AWTEvent通常描述API中预定义的系统或核心事件,而不是使用类似“instanceof”的东西由ID标识。另一方面,EventObject用于创建更多通用或自定义事件。通常,这是我开始的地方。然后我定义了一个监听器接口来与它一起实现EventListener – MadProgrammer

回答

2

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

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

要产生你只需使用类似的事件:

firePropertyChange("selectionRectangle", oldRectangle, newRectangle); 

然后你可以使用一个PropertyChangeListener监听“selectionRectangle”的转变。

1

Javadoc for AWTEvent说:

java.awt.event包应该定义事件ID之外定义这根的AWTEvent类的

子类值大于由RESERVED_ID_MAX定义的值越大。

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

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

最后,我会考虑继承ComponentEvent而不是AWTEvent,因为事件的来源是Component,而不是Object