简体   繁体   English

Java/Swing:单击矩形时创建文本字段

[英]Java/Swing: Create text fields upon clicking a rectangle

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.我正在创建一个应用程序,用户可以在其中上传图像,使用鼠标在图像顶部绘制矩形进行注释,单击每个矩形时,右侧会显示一个空文本字段,如图所示,供用户附加评论到那个特定的区域。 在此处输入图片说明 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?我的猜测是我必须在 DrawingArea 类中创建 JTextFields 并以某种方式将它传递给 ImageAnnotator 类? or I have to import the ImageAnnotator inside the DrawingArea and input the TextFields there directly?或者我必须在 DrawingArea 中导入 ImageAnnotator 并直接在那里输入 TextFields? 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.我也无法在屏幕上显示图像名称,即使我将它传递给 JLabel。 Any help is appreciated.任何帮助表示赞赏。

DrawingArea.java:绘图区.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: 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:盒子.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;
    }
    
}

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您可能需要的第一件事是某种侦听器或观察者,可用于在选择Box时通知相关方

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.这将由管理绘图和输入组件的 UI 的父部分实现,当触发时,感兴趣的一方将根据其需要采取适当的行动。

Next, we need to add some management code for the DrawingArea to manage the listener support.接下来,我们需要为DrawingArea添加一些管理代码来管理监听器支持。 This is made easier as Swing components have some handy support由于 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);
    // 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.在检测到阻力之前,我们不会创建新的shape ,这为我们的决策过程提供了一些回旋余地。
  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如果用户“点击”了组件,我们扫描可用的形状并确定用户是否点击了一个,如果一个被点击,我们调用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. didSelect方法是一个简单的机会,可以处理您想要“选择”特定框时需要做的所有事情。 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);
}

你可以用,当你点击一个广场去,你既可以,捕获鼠标的位置,并做一些数学和构建jpanel用一种无形的jtextbox ,或做同样的事情,但产卵时,提请箱面板。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM