我創建了一個圖形組件,允許您查看圖像,並允許您選擇圖像的一部分:圖像的一部分的選擇是通過在該圖像上繪製矩形來完成的(使用拖放和-下降)。如何繪製新的矩形時觸發自定義事件?
爲了達到此目的,我使用了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標識用於創建事件的兩種超級類型:EventObject和AWTEvent。這篇文章指出:
通常情況下,您擴展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
?
創建事件類時,必須保證事件是 不變的。事件生成器將在偵聽器中共享相同的事件實例 ;所以確保任何一個監聽器都不能更改事件的狀態。
這是怎麼回事?
AWTEvent和EventObject之間有一個普遍的區別,AWTEvent通常描述API中預定義的系統或核心事件,而不是使用類似「instanceof」的東西由ID標識。另一方面,EventObject用於創建更多通用或自定義事件。通常,這是我開始的地方。然後我定義了一個監聽器接口來與它一起實現EventListener – MadProgrammer