我正在使用Java Swing。
我需要一个允许用户缩放绘图的应用程序。
图纸是在自定义的JPanel paintComponent方法内完成的。
一张图是一个drawRect方法调用。
在应用程序中,有两种方法(让我称之为"模式")缩放绘图:
调用方法时,只需增加图形的宽度和高度即可。例如:
graphics.drawRect(x, y, width + addX, height + addY);
其中addX
和addY
是用户鼠标拖动手势的偏移量。
在绘制绘图之前,使用/更改处理绘图缩放的AffineTransform。例如:
graphics.transform(at);
graphics.drawRect(x, y, width, height);
两种缩放模式组合在一起!
用户将鼠标拖动到图形边缘以缩放图形。
用户每次通过两个按钮选择启用哪种模式:第一种模式的1按钮("绘制比例")和第二种模式的1按钮(" AffineTransform scale" )。
到目前为止,这是我的代码:
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对象相应地改变用户的拖动手势,这样当变换应用于绘图时,绘图将缩放用户鼠标所在的位置。
为清晰起见,代码将绘图绘制在红色矩形内。缩放模式后的绘图大小必须仍然在红色矩形内,但它不是,这就是问题。
以下是演示此问题的屏幕截图:
要重新创建问题,您可以执行以下步骤: