Java/Swing:在单击矩形时创建文本框

huangapple go评论73阅读模式
英文:

Java/Swing: Create text fields upon clicking a rectangle

问题

以下是你提供的代码的中文翻译部分:

DrawingArea.java:

public class DrawingArea extends JPanel
{
    // ...(其他代码)

    public void addRectangle(Rectangle rectangle, Color color)
    {
        // ...(其他代码)
    }

    public void clear()
    {
        // ...(其他代码)
    }
    
    public void loadImage() {
        // ...(其他代码)
    }
    
    public static BufferedImage scaleImage(int w, int h, BufferedImage img) throws Exception {
        // ...(其他代码)
    }

    class MyMouseListener extends MouseInputAdapter
    {
        // ...(其他代码)
    }        
}

ImageAnnotator.java:

public class ImageAnnotator extends JFrame {

    // ...(其他代码)

    public ImageAnnotator(Model m) {
        // ...(其他代码)
    }
}

Box.java:

public class Box {
    // ...(其他代码)
}

请注意,这只是你提供代码的翻译部分,我已经根据你的要求移除了注释以及非翻译的内容。如果你有其他问题或需要进一步的帮助,请随时提问。

英文:

I am creating an app where the user can upload an image, draw rectangles on top of the image using the mouse for annotation, and each rectangle upon click will display an empty text field on the right as shown in pic for the user to attach comments to that certain region. Java/Swing:在单击矩形时创建文本框 Right now the program will upload the image and lets the user draw rectangles on top of it (the left side of the image shown above is implemented), but I'm having trouble figuring out how to make the program create and display an empty text field when the user clicks on a rectangle for the first time (implementing the right side of the image shown). The rectangle will also be highlighted in red when selected, and if a comment is already there, the comment should be in the text box upon click. My guess is I will have to create the JTextFields in the DrawingArea class and somehow pass it over to the ImageAnnotator class? or I have to import the ImageAnnotator inside the DrawingArea and input the TextFields there directly? Or maybe there is a connection that I'm not making. I'm also having trouble displaying the image name on the screen, even though I'm passing it to the JLabel. Any help is appreciated.

DrawingArea.java:

public class DrawingArea extends JPanel
	{
		private final static int AREA_SIZE = 490;
		private BufferedImage image =
			new BufferedImage(AREA_SIZE, AREA_SIZE, BufferedImage.TYPE_INT_ARGB);
		private Rectangle shape;
		private ArrayList<Box> rectangles = new ArrayList<Box>();
		public String imageName = ""; // this will store the image/file name

		public DrawingArea()
		{
			setBackground(Color.WHITE);

			MyMouseListener ml = new MyMouseListener();
			addMouseListener(ml);
			addMouseMotionListener(ml);
		}

		@Override
		protected void paintComponent(Graphics g)
		{
			super.paintComponent(g);

			//  Custom code to support painting from the BufferedImage

			if (image != null)
			{
				g.drawImage(image, 0, 0, null);
			}
			
			Color foreground = g.getColor();


			for (Box b : rectangles)
			{
				g.setColor( b.getForeground() );
				Rectangle r = b.getRectangle();
				g.drawRect(r.x, r.y, r.width, r.height);
			}

			//  Paint the Rectangle as the mouse is being dragged

			if (shape != null)
			{
				Graphics2D g2d = (Graphics2D)g;
				g2d.draw( shape );
			}
		}

		public void addRectangle(Rectangle rectangle, Color color)
		{
			//  Draw the Rectangle onto the BufferedImage

			Box b = new Box(color, rectangle);
			rectangles.add( b );
			repaint();
		}

		public void clear()
		{
			rectangles.clear();
			repaint();
		}
		
		public void loadImage() {
            ...
            this.imageName = f.getName(); //this is where I pass the file name
            ...
        }
		
		public static BufferedImage scaleImage(int w, int h, BufferedImage img) throws Exception {...}

		class MyMouseListener extends MouseInputAdapter
		{
			private Point startPoint;

			public void mousePressed(MouseEvent e)
			{
				startPoint = e.getPoint();
				shape = new Rectangle();
			}

			public void mouseDragged(MouseEvent e)
			{
				int x = Math.min(startPoint.x, e.getX());
				int y = Math.min(startPoint.y, e.getY());
				int width = Math.abs(startPoint.x - e.getX());
				int height = Math.abs(startPoint.y - e.getY());

				shape.setBounds(x, y, width, height);
				repaint();
			}

			public void mouseReleased(MouseEvent e)
			{
				if (shape.width != 0 || shape.height != 0)
				{
					addRectangle(shape, e.getComponent().getForeground());
				}

				shape = null;
			}
		}		
	}

ImageAnnotator.java:

public class ImageAnnotator extends JFrame {

	private JPanel contentPane;
	Model model;
	private JLabel ImageName;
	DrawingArea drawingArea;
	ButtonPanel buttonPanel;
	GroupLayout gl_contentPane;
	private JLabel lblNewLabel;
	/**
	 * Create the frame.
	 */
	public ImageAnnotator(Model m) {
		super();
		this.model = m;
		setTitle("Image Annotator");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 850, 646);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		setContentPane(contentPane);
		drawingArea = new DrawingArea();
		
		ImageName = new JLabel(drawingArea.imageName); // here I'm trying to set the filename
		buttonPanel = new ButtonPanel( drawingArea );
		
		lblNewLabel = new JLabel("Comments");
		lblNewLabel.setFont(new Font("Times New Roman", Font.BOLD, 17));
		
		gl_contentPane = new GroupLayout(contentPane);
		gl_contentPane.setHorizontalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_contentPane.createSequentialGroup()
					.addContainerGap()
					.addComponent(drawingArea, GroupLayout.PREFERRED_SIZE, 490, GroupLayout.PREFERRED_SIZE)
					.addPreferredGap(ComponentPlacement.RELATED, 128, Short.MAX_VALUE)
					.addComponent(lblNewLabel)
					.addGap(117))
				.addGroup(gl_contentPane.createSequentialGroup()
					.addGap(121)
					.addGroup(gl_contentPane.createParallelGroup(Alignment.TRAILING)
						.addComponent(buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addComponent(ImageName, GroupLayout.PREFERRED_SIZE, 254, GroupLayout.PREFERRED_SIZE))
					.addContainerGap(451, Short.MAX_VALUE))
		);
		gl_contentPane.setVerticalGroup(
			gl_contentPane.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_contentPane.createSequentialGroup()
					.addContainerGap()
					.addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING)
						.addComponent(lblNewLabel)
						.addGroup(gl_contentPane.createSequentialGroup()
							.addComponent(drawingArea, GroupLayout.PREFERRED_SIZE, 490, GroupLayout.PREFERRED_SIZE)
							.addPreferredGap(ComponentPlacement.RELATED)
							.addComponent(ImageName, GroupLayout.PREFERRED_SIZE, 26, GroupLayout.PREFERRED_SIZE)))
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
					.addContainerGap(27, Short.MAX_VALUE))
		);
		contentPane.add(drawingArea);
		contentPane.setLayout(gl_contentPane);
	}

Box.java:

public class Box {
	int bWidth, bHeight, bX, bY;
    String bImageName, bComment;
    
    Color foreground;
	Rectangle rectangle;
    
    public Box(int width, int height) {
    	bWidth = width;
    	bHeight = height;
    }
    
    public Box(Color foreground, Rectangle rectangle) {
    	this.foreground = foreground;
		this.rectangle = rectangle;
    }
    
    public void setImageName(String imageName) { bImageName = imageName; }
    public String getImageName() { return bImageName; }
    
    public void setComment(String comment) { bComment = comment; }
    public String getComment() { return bComment; }
    
    public void setX(int x) { bX = x; }
    public int getX() { return bX; }
    
    public void setY(int y) { bY = y; }
    public int getY() { return bY; }
    
    public Color getForeground()
	{
		return foreground;
	}

	public void setForeground(Color foreground)
	{
		this.foreground = foreground;
	}

	public Rectangle getRectangle()
	{
		return rectangle;
	}
	
}

答案1

得分: 2

首先,你可能需要一种监听器或观察者,用于在选择“Box”时通知感兴趣的方。

public interface BoxSelectionListener extends EventListener {
    public void didSelect(Box box);
}

这将由 UI 的父部分实现,该父部分管理绘图和输入组件。当触发时,感兴趣的方将根据其需求采取适当的操作。

接下来,我们需要为 DrawingArea 添加一些管理代码以支持监听器。由于 Swing 组件提供了一些便捷的支持,这变得更加容易。

public void addBoxSelectionListener(BoxSelectionListener listener) {
    listenerList.add(BoxSelectionListener.class, listener);
}

public void removeBoxSelectionListener(BoxSelectionListener listener) {
    listenerList.remove(BoxSelectionListener.class, listener);
}

protected void fireBoxSelected(Box box) {
    BoxSelectionListener[] listeners = listenerList.getListeners(BoxSelectionListener.class);
    // 通常,我会创建一个事件对象,该对象会将源(this)和 Box 封装在一起,
    // 但如果没有监听器,这样做有点浪费,所以在这些情况下提前返回
    if (listeners.length == 0) {
        return;
    }
    for (BoxSelectionListener listener : listeners) {
        listener.didSelect(box);
    }
}

好的,非常简单,你可以添加或移除监听器,并在需要时触发事件。

接下来是“稍微”难一些的部分。当鼠标按下并释放时,你需要做出一些决策,是要选择一个框还是要绘制一个新的框。

class MyMouseListener extends MouseInputAdapter {

    private Point startPoint;

    public void mousePressed(MouseEvent e) {
        // 标记剪辑点
        startPoint = e.getPoint();
    }

    public void mouseDragged(MouseEvent e) {
        // 仅在拖动开始时创建形状
        if (shape == null) {
            shape = new Rectangle();
        }
        int x = Math.min(startPoint.x, e.getX());
        int y = Math.min(startPoint.y, e.getY());
        int width = Math.abs(startPoint.x - e.getX());
        int height = Math.abs(startPoint.y - e.getY());

        shape.setBounds(x, y, width, height);
        repaint();
    }

    public void mouseReleased(MouseEvent e) {
        if (shape != null) {
            if (shape.width != 0 || shape.height != 0) {
                addRectangle(shape, e.getComponent().getForeground());
            }
        } else {
            for (Box b : rectangles) {
                if (b.getRectangle().contains(e.getPoint())) {
                    didSelect(b);
                    break;
                }
            }
        }

        startPoint = null;
        shape = null;
    }
}

对你的代码进行了一些修改。

  1. 直到检测到拖动,我们不会创建新的 shape,这使得我们在决策过程中有一些灵活性。
  2. 如果用户“点击”了组件,我们会遍历可用的形状,确定用户是否点击了其中一个,如果点击了其中一个,我们调用 didSelect

为什么这样做?主要原因是允许我们检测是否有人创建了重叠的矩形(或多或少),但幸运的是,他们不太可能这样做,因为这会使选择变得非常困难。

didSelect 方法是处理选择特定框所需的所有事情的简单机会。这样做将功能解耦,允许你决定何时可能调用它。

public void didSelect(Box box) {
    // 可能将其分配给“已分配”或“已选定”属性
    // 以便可以以不同的方式绘制
    // 现在我们想要通知某种类型的监听器,以便
    // 它可以根据需要更新 UI
    
    fireBoxSelected(box);
}
英文:

The first thing you probably need is some kind of listener or observer that can be used to notify interested parties when the Box is selected

public interface BoxSelectionListener extends EventListener {
public void didSelect(Box box);
}

This would be implemented by the parent part of the UI that is managing both the drawing and input components, when triggered, the interested party would then take appropriate action based on its needs.

Next, we need to add some management code for the DrawingArea to manage the listener support. This is made easier as Swing components have some handy support

public void addBoxSelectionListener(BoxSelectionListener listener) {
listenerList.add(BoxSelectionListener.class, listener);
}
public void removeBoxSelectionListener(BoxSelectionListener listener) {
listenerList.remove(BoxSelectionListener.class, listener);
}
protected void fireBoxSelected(Box box) {
BoxSelectionListener[] listeners = listenerList.getListeners(BoxSelectionListener.class);
// Normally, I'd create a event object, which would wrap the source (this) and
// the Box together, but if there are no listeners, it's a bit of
// a waste to do so, so I return early in those cases
if (listeners.length == 0) {
return;
}
for (BoxSelectionListener listener : listeners) {
listener.didSelect(box);
}
}

Okay, pretty simple, you can add or remove a listener and trigger the event to fire when you need to.

Okay, now the "slightly" harder bit. When the mouse is pressed and released, you need to make some decisions about what to do, do you want to select a box or do you want to draw a new one.

class MyMouseListener extends MouseInputAdapter {
private Point startPoint;
public void mousePressed(MouseEvent e) {
// Mark the clip point
startPoint = e.getPoint();
}
public void mouseDragged(MouseEvent e) {
// Only create the shape when dragging starts
if (shape == null) {
shape = new Rectangle();
}
int x = Math.min(startPoint.x, e.getX());
int y = Math.min(startPoint.y, e.getY());
int width = Math.abs(startPoint.x - e.getX());
int height = Math.abs(startPoint.y - e.getY());
shape.setBounds(x, y, width, height);
repaint();
}
public void mouseReleased(MouseEvent e) {
if (shape != null) {
if (shape.width != 0 || shape.height != 0) {
addRectangle(shape, e.getComponent().getForeground());
}
} else {
for (Box b : rectangles) {
if (b.getRectangle().contains(e.getPoint())) {
didSelect(b);
break;
}
}
}
startPoint = null;
shape = null;
}
}

So, some modifications to your code.

  1. Until the drag is detected, we don't create a new shape, this allows us some wiggle room in our decision making process.
  2. If the user "clicked" the component, we scan through the available shapes and determine if the user clicked one, if one was clicked we call didSelect

Why do we do this? The main reason for doing this is it allows use to detect when some one is creating overlapping rectangles (more or less), but with any luck they won't do that, because it makes selections super difficult.

The didSelect method is a simple opportunity to handle all the things you need to do when you want to "select" a particular box. This decouples the functionality and allow you to make decisions about when it might be called

public void didSelect(Box box) {
// Probably assign this to a "assigned" or "selected" property
// so it can painted differently
// And now we want to notify some kind of listener so that
// it can update the UI as required
fireBoxSelected(box);
}

答案2

得分: 0

当您点击一个方块时,您可以选择捕捉鼠标的位置,进行一些数学计算,并构建一个带有不可见jtextboxjpanel;或者可以在绘制方块时执行相同的操作,但在那时生成面板。

英文:

You could go with when you click on a square, you could either, capture the mouse's location and do some math and build a jpanel with an invisible jtextbox, or do the same thing but spawn the panel when they draw the box.

huangapple
  • 本文由 发表于 2020年9月27日 08:33:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/64083736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定