1

我正在研究 Java Swing。

我需要一个让用户缩放绘图的应用程序。

绘图是在自定义JPanelpaintComponent方法中完成的。

可以说,绘图是一个drawRect方法调用。

在应用程序中有两种缩放绘图的方法(让我称之为“模式”):

  1. 当调用它的方法时,简单地增加绘图的宽度和高度。例如:

    graphics.drawRect(x, y, width + addX, height + addY);
    

    其中addXaddY是用户鼠标拖动手势的偏移量。

  2. 在绘制绘图之前使用/更改处理绘图缩放的AffineTransform 。例如:

    graphics.transform(at);
    graphics.drawRect(x, y, width, height);
    

两种缩放模式结合在一起!

用户在绘图的边缘拖动鼠标以缩放绘图。

用户每次通过两个按钮选择启用哪种模式:第一个模式(“绘制比例”)的一个按钮和第二个模式(“AffineTransform 比例”)的一个按钮。

到目前为止,这是我的代码:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main {

    //The panel which handles drawing:
    private static class PaintPanel extends JPanel {
        private final double drawX, drawY;
        private final AffineTransform at;
        private double drawWidth, drawHeight;

        public PaintPanel(final double drawX,
                          final double drawY,
                          final double drawWidth,
                          final double drawHeight) {
            at = new AffineTransform();
            this.drawX = drawX;
            this.drawY = drawY;
            this.drawWidth = drawWidth;
            this.drawHeight = drawHeight;
        }

        public AffineTransform getAffineTransform() {
            return at;
        }

        public double getDrawX() {
            return drawX;
        }

        public double getDrawY() {
            return drawY;
        }

        public double getDrawWidth() {
            return drawWidth;
        }

        public double getDrawHeight() {
            return drawHeight;
        }

        public double getTotalWidth() {
            return getDrawWidth() * getAffineTransform().getScaleX();
        }

        public double getTotalHeight() {
            return getDrawHeight() * getAffineTransform().getScaleY();
        }

        @Override
        public void paintComponent(final Graphics g) {
            super.paintComponent(g);
            final Graphics2D g2d = (Graphics2D) g.create();

            //Red rectangle for clarity.
            g.setColor(Color.RED);
            g.drawRect((int)getDrawX(), (int)getDrawY(), (int)getTotalWidth()-1, (int)getTotalHeight()-1);

            //The drawing!
            g2d.transform(getAffineTransform());
            g2d.drawRect((int)getDrawX(), (int)getDrawY(), (int)getDrawWidth()-1, (int)getDrawHeight()-1);
            g2d.drawOval((int)getDrawX(), (int)getDrawY(), (int)getDrawWidth()-1, (int)getDrawHeight()-1);

            g2d.dispose();
        }

        //The scale(...) method scales the drawing.
        //The scale(...) method needs changes.
        public void scale(final boolean drawScale,
                          final double addX,
                          final double addY) {
            if (drawScale) {
                drawWidth += (addX / getAffineTransform().getScaleX());
                drawHeight += (addY / getAffineTransform().getScaleY());
            }
            else {
                final double tmpAddX = addX / getAffineTransform().getScaleX(),
                             tmpAddY = addY / getAffineTransform().getScaleY();
                final double sx = (getDrawWidth() + tmpAddX) / getDrawWidth(),
                             sy = (getDrawHeight() + tmpAddY) / getDrawHeight();

                getAffineTransform().scale(sx, sy);

                final double tmpAddX2 = addX / getAffineTransform().getScaleX(),
                             tmpAddY2 = addY / getAffineTransform().getScaleY();
                final double sx2 = (getDrawWidth() + tmpAddX2) / getDrawWidth(),
                             sy2 = (getDrawHeight() + tmpAddY2) / getDrawHeight();

                getAffineTransform().translate(getDrawX() - getDrawX()*sx2,
                                               getDrawY() - getDrawY()*sy2);
            }
            repaint();
        }
    }

    private static boolean drawScale;

    public static void main(final String[] args) {
        final Dimension dim = new Dimension(300, 300);
        final int drawOff = 100;

        final PaintPanel paintPanel = new PaintPanel(drawOff/2, drawOff/2, dim.width - 2*drawOff, dim.height - 2*drawOff);
        paintPanel.setPreferredSize(dim);
        final MouseAdapter ma = new MouseAdapter() {
            private Point dragLocation;
            private boolean dragX, dragY;

            //Tells if the width is dragged:
            private boolean isDragX(final Point loc) {
                return (loc.getX() > paintPanel.getTotalWidth()-10+paintPanel.getDrawX()
                        && loc.getX() < paintPanel.getTotalWidth()+10+paintPanel.getDrawX()
                        && loc.getY() < paintPanel.getTotalHeight()+paintPanel.getDrawY()+10
                        && loc.getY() > paintPanel.getDrawY());
            }

            //Tells if the height is dragged:
            private boolean isDragY(final Point loc) {
                return (loc.getY() > paintPanel.getTotalHeight()-10+paintPanel.getDrawY()
                        && loc.getY() < paintPanel.getTotalHeight()+10+paintPanel.getDrawY()
                        && loc.getX() < paintPanel.getTotalWidth()+paintPanel.getDrawX()+10
                        && loc.getX() > paintPanel.getDrawX());
            }

            //Only used to set the cursor:
            @Override
            public void mouseMoved(final MouseEvent mevt) {
                final Point loc = mevt.getPoint();
                if (isDragX(loc) && isDragY(loc))
                    paintPanel.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
                else if (isDragX(loc))
                    paintPanel.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
                else if (isDragY(loc))
                    paintPanel.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
                else if (paintPanel.getCursor() != Cursor.getDefaultCursor())
                    paintPanel.setCursor(Cursor.getDefaultCursor());
            }

            //Determines (on mouse click) which sides are dragged:
            @Override
            public void mousePressed(final MouseEvent mevt) {
                final Point loc = mevt.getPoint();
                dragX = isDragX(loc);
                dragY = isDragY(loc);
                dragLocation = loc;
            }

            //Scales while dragging:
            @Override
            public void mouseDragged(final MouseEvent mevt) {
                final Point loc = mevt.getPoint();
                if (dragX && dragY)
                    paintPanel.scale(drawScale, loc.getX()-dragLocation.getX(), loc.getY()-dragLocation.getY());
                else if (dragX)
                    paintPanel.scale(drawScale, loc.getX()-dragLocation.getX(), 0);
                else if (dragY)
                    paintPanel.scale(drawScale, 0, loc.getY()-dragLocation.getY());
                dragLocation = loc;
            }
        };
        paintPanel.addMouseListener(ma);
        paintPanel.addMouseMotionListener(ma);

        final JButton drawScaleButton = new JButton("Draw scale"),
                      affineTransformScaleButton = new JButton("AffineTransform scale");
        drawScaleButton.addActionListener(e -> {
            drawScale = true;
            drawScaleButton.setEnabled(false);
            affineTransformScaleButton.setEnabled(true);
        });
        affineTransformScaleButton.addActionListener(e -> {
            drawScale = false;
            drawScaleButton.setEnabled(true);
            affineTransformScaleButton.setEnabled(false);
        });
        drawScale = false;
        drawScaleButton.setEnabled(true);
        affineTransformScaleButton.setEnabled(false);

        final JPanel buttons = new JPanel(/*FlowLayout*/);
        buttons.add(affineTransformScaleButton);
        buttons.add(drawScaleButton);

        final JPanel contents = new JPanel(new BorderLayout());
        contents.add(buttons, BorderLayout.PAGE_START);
        contents.add(paintPanel, BorderLayout.CENTER);

        final JFrame frame = new JFrame("Scaling shape test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contents);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

问题是我无法正确组合这两种模式。我需要 AffineTransform 对象根据用户的拖动手势进行相应更改,以便在将变换应用于绘图时,绘图会在用户鼠标所在的位置缩放。

为清楚起见,代码将图形绘制在红色矩形内。缩放模式后的绘图大小必须仍在红色矩形内,但不是,这就是问题所在。

这是演示问题的屏幕截图:

问题DemoImage

要重新创建问题,您可以执行以下步骤:

  1. 拖动绘图以放大它,直到它到达框架的两侧。
  2. 选择“绘制比例”。
  3. 尽可能小地拖动绘图(但也是可见的)。
  4. 选择“仿射变换比例”。
  5. 重复上述步骤一两次,问题应该是可见的。
4

0 回答 0