2015-07-19 44 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