我正在尝试创建一个图形组件,该组件允许我在选定的图像上绘制一个矩形(必须使用拖放操作绘制矩形):该组件的目的是获取对象的坐标和大小绘制矩形第二个目标是提供一种可以轻松集成在图形用户界面中的组件。

this example的作者创建了JLabel的子类以绘制图像,然后他们在此子类的实例中添加了MouseInputAdapter以便处理矩形的绘制。

我受到那个例子的启发,区别在于我创建了JPanel类的子类:我将其命名为FigurePanel类。然后,我进行了一些更改以提供以下功能:

  • 如果图像大于FigurePanel实例,则必须出现滚动条;否则,滚动条将显示。
  • 如果图像小于FigurePanel实例,则此图像必须放在面板的中央;
  • 当用户绘制矩形时,它不应延伸超出图像的范围。

  • 这是FigurePanel类的源代码。
    package imageselectionproject;
    
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.Rectangle;
    import java.awt.event.MouseEvent;
    import javax.swing.JPanel;
    import javax.swing.event.MouseInputAdapter;
    
    
    public class FigurePanel extends JPanel
    {
        private Image backgroundImage = null;
        private Rectangle imageLimits = new Rectangle(0, 0, getWidth(), getHeight());
    
        private Rectangle currentRect = null;
        private Rectangle rectToDraw = null;
        private final Rectangle previousRectDrawn = new Rectangle();
    
    
        public FigurePanel()
        {
            setOpaque(true);
    
            SelectionListener listener = new SelectionListener();
            addMouseListener(listener);
            addMouseMotionListener(listener);
        }
    
        @Override
        public Dimension getPreferredSize()
        {
            return backgroundImage == null ? super.getPreferredSize() : new Dimension(backgroundImage.getWidth(this), backgroundImage.getHeight(this));
        }
    
        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g); //paints the background and image
    
            if (backgroundImage != null)
            {
                g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
            }
    
            // 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);
            }
        }
    
    
        public void setImage(Image image)
        {
            int x = 0;
            int y = 0;
    
            if (image != null)
            {
                backgroundImage = image;
    
                // The following instructions are used to center the image on the panel.
                /*x = (getSize().width - image.getWidth(this)) / 2;
                y = (getSize().height - image.getHeight(this)) / 2;
    
                if (x < 0) x = 0;
                if (y < 0) y = 0;*/
            }
            else
            {
                backgroundImage = null;
            }
    
            currentRect = null;
    
            setSize(getPreferredSize());
            imageLimits.setBounds(x, y, getWidth(), getHeight());
            System.out.println("imageLimits = " + imageLimits);
    
            repaint();
        }
    
        private void updateDrawableRect()
        {
            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 should not extend beyond the boundaries of the image.
            if (x < imageLimits.x)
            {
                width -= (imageLimits.x - x);
                x = imageLimits.x;
            }
            else if ((x + width) > imageLimits.x + imageLimits.width)
            {
                width = imageLimits.x + imageLimits.width - x;
            }
            if (y < imageLimits.y)
            {
                height -= (imageLimits.y - y);
                y = imageLimits.y;
            }
            if ((y + height) > imageLimits.y + imageLimits.height)
            {
                height = imageLimits.y + imageLimits.height - 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);
            }
        }
    
        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();
                repaint();
            }
    
            @Override
            public void mouseDragged(MouseEvent e)
            {
                updateSize(e.getX(), e.getY());
            }
    
            @Override
            public void mouseReleased(MouseEvent e)
            {
                updateSize(e.getX(), e.getY());
            }
    
            /*
             * 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(int x, int y)
            {
                currentRect.setSize(x - currentRect.x, y - currentRect.y);
                updateDrawableRect();
    
                Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
                repaint(totalRepaint.x, totalRepaint.y,
                        totalRepaint.width, totalRepaint.height);
            }
        }
    
    }
    

    方法setImage用于设置新图像,因此它调用方法repaint重绘图形组件。在上面显示的代码中,我禁用了(通过注释)使图像居中的说明:这样,该组件似乎可以正常工作。

    相反,如果启用这些说明,则当图像小于面板本身时,图像会正确地定位在面板的中央,但是,我遇到了以下问题:假设当前显示的图像大于面板,如果我决定加载的新图像小于当前显示的图像,则不显示新图像;如果我尝试重新加载新图像,则会显示它。

    为什么会出现此问题?怎么解决呢?

    我还创建了FigurePanelTest类以测试FigurePanel类。
    package imageselectionproject;
    
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    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 FlowLayout());
            container.add(imageScrollPane);
            container.add(imageButton);
    
            setSize(600, 400);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
    
    }
    

    这是主要的。
    public static void main(String args[]) {
            /* Create and display the form */
            java.awt.EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new FigurePanelTest().setVisible(true);
                }
            });
        }
    

    对于Andrew,只有一个程序:
    import java.awt.Color;
    import java.awt.Container;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.MouseEvent;
    import java.awt.image.BufferedImage;
    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.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.event.MouseInputAdapter;
    
    public class TestDrawPanel {
       public static void main(String args[]) {
          /* Create and display the form */
          java.awt.EventQueue.invokeLater(new Runnable() {
             public void run() {
                new FigurePanelTest().setVisible(true);
             }
          });
       }
    }
    
    class FigurePanelTest extends JFrame {
       public FigurePanelTest() {
          final FigurePanel imagePanel = new FigurePanel();
    
          final 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 FlowLayout());
          container.add(imageScrollPane);
          container.add(imageButton);
    
          setSize(600, 400);
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       }
    
    }
    
    class FigurePanel extends JPanel {
       private Image backgroundImage = null;
       private Rectangle imageLimits = new Rectangle(0, 0, getWidth(), getHeight());
    
       private Rectangle currentRect = null;
       private Rectangle rectToDraw = null;
       private final Rectangle previousRectDrawn = new Rectangle();
    
       public FigurePanel() {
          setOpaque(true);
    
          SelectionListener listener = new SelectionListener();
          addMouseListener(listener);
          addMouseMotionListener(listener);
       }
    
       @Override
       public Dimension getPreferredSize() {
          return backgroundImage == null ? super.getPreferredSize()
                : new Dimension(backgroundImage.getWidth(this),
                      backgroundImage.getHeight(this));
       }
    
       @Override
       protected void paintComponent(Graphics g) {
          super.paintComponent(g); // paints the background and image
    
          if (backgroundImage != null) {
             g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
          }
    
          // 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);
          }
       }
    
       public void setImage(Image image) {
          int x = 0;
          int y = 0;
    
          if (image != null) {
             backgroundImage = image;
    
             // The following instructions are used to center the image on the
             // panel.
             /*
              * x = (getSize().width - image.getWidth(this)) / 2; y =
              * (getSize().height - image.getHeight(this)) / 2;
              *
              * if (x < 0) x = 0; if (y < 0) y = 0;
              */
          } else {
             backgroundImage = null;
          }
    
          currentRect = null;
    
          setSize(getPreferredSize());
          imageLimits.setBounds(x, y, getWidth(), getHeight());
          System.out.println("imageLimits = " + imageLimits);
    
          repaint();
       }
    
       private void updateDrawableRect() {
          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 should not extend beyond the boundaries of the image.
          if (x < imageLimits.x) {
             width -= (imageLimits.x - x);
             x = imageLimits.x;
          } else if ((x + width) > imageLimits.x + imageLimits.width) {
             width = imageLimits.x + imageLimits.width - x;
          }
          if (y < imageLimits.y) {
             height -= (imageLimits.y - y);
             y = imageLimits.y;
          }
          if ((y + height) > imageLimits.y + imageLimits.height) {
             height = imageLimits.y + imageLimits.height - 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);
          }
       }
    
       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();
             repaint();
          }
    
          @Override
          public void mouseDragged(MouseEvent e) {
             updateSize(e.getX(), e.getY());
          }
    
          @Override
          public void mouseReleased(MouseEvent e) {
             updateSize(e.getX(), e.getY());
          }
    
          /*
           * 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(int x, int y) {
             currentRect.setSize(x - currentRect.x, y - currentRect.y);
             updateDrawableRect();
    
             Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
             repaint(totalRepaint.x, totalRepaint.y, totalRepaint.width,
                   totalRepaint.height);
          }
       }
    
    }
    

    最佳答案

    问题是在setImage()中计算x和y,这不是正确计算面板的中心,我是通过多次加载大小图像后检查x和y的值来表示的

    但是我尝试将居中的图像放在paintComponent内,并且效果很好

     if (backgroundImage != null) {
            imageLimits.x = (this.getWidth() - backgroundImage.getWidth(this)) / 2;
            imageLimits.y = (this.getHeight() - backgroundImage.getHeight(this)) / 2;
            g.drawImage(backgroundImage, imageLimits.x, imageLimits.y, this);
        }
    

    09-10 08:50
    查看更多