图形上下文在第一次绘制时未对齐

我一直在为另一个问题找到答案,并遇到了一个我以前从未见过的奇怪问题……

基本上,程序使用AffineTransform来提供Graphics元素的翻译,缩放和旋转,简单的东西,之前完成了一千次

问题是,当屏幕第一次出现时,输出不是应该的位置,但是一旦我触摸其中一个控件(调整其中一个幻灯片),它就会跳到正确的位置。

滑块滑块

根据屏幕截图, Graphics内容似乎被其他控件的数量错位。

如果我从GUI中删除控件,它将出现在正确的位置(中间)。 如果我调整窗口大小,它不能解决问题,它只会在其中一个滑块触发DrawPane上的repaintDrawPane

我已经在输出中添加了诊断信息,并且所有值都是相同的 – 也就是说,它们在程序首次启动时以及将所有滑块值调整为初始值时打印相同的值。

如果我从AffineTransform删除setRotationsetScale调用,则不会修复它。 如果我删除了setTranslation ,那么在面板更新之前,方块最初不会被绘制(它在屏幕外绘制)

如果我使用Graphics2D g2d = (Graphics)g; 而不是g.create() ,相同的结果(是的,我在paintComponent方法退出之前重置了转换;))

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.geom.AffineTransform; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class Parker { public static void main(String[] args) { new Parker(); } public Parker() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new ControlPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class ControlPane extends JPanel { private JSlider slider; //declare slider private DrawPane myPanel; public ControlPane() { setLayout(new BorderLayout()); myPanel = new DrawPane(); myPanel.setBackground(Color.cyan); //change background color slider = new JSlider(SwingConstants.VERTICAL, 0, 400, 100);// restrains the slider from scaling square to 0-300 pixels slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels slider.setPaintTicks(true); //this actually paints the ticks on the screen slider.addChangeListener( new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { myPanel.setScale(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen } } ); JSlider rotate = new JSlider(SwingConstants.VERTICAL, 0, 720, 0); rotate.setMajorTickSpacing(20); //will set tick marks every 10 pixels rotate.setPaintTicks(true); //this actually paints the ticks on the screen rotate.addChangeListener( new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { JSlider slider = (JSlider) e.getSource(); myPanel.setAngle(slider.getValue()); } } ); add(slider, BorderLayout.WEST); add(rotate, BorderLayout.EAST); add(myPanel); slider.setValue(0); slider.setValue(100); rotate.setValue(0); } } public class DrawPane extends JPanel { private double scale = 1; private double angle = 0; private final int rectWidth = 20; private final int rectHeight = 20; @Override protected void paintComponent(Graphics g)//paints obj on the screen { super.paintComponent(g); //prepares graphic object for drawing int originX = getWidth() / 2; int originY = getHeight() / 2; int xOffset = -(rectWidth / 2); int yOffset = -(rectHeight / 2); g.setColor(Color.BLACK); Graphics2D g2d = (Graphics2D) g.create(); AffineTransform at = new AffineTransform(); at.translate(originX, originY); g2d.setTransform(at); g2d.scale(scale, scale); g2d.rotate(Math.toRadians(angle), 0, 0); g2d.fillRect(xOffset, yOffset, rectWidth, rectHeight); g2d.dispose(); g.setColor(Color.RED); g.drawRect(originX + xOffset, originY + yOffset, rectWidth, rectWidth); } public void setAngle(double angle) { this.angle = angle; repaint(); } public void setScale(int scale) { // Scaling is normalized so that 1 = 100% this.scale = (scale / 100d); repaint(); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } } } 

基本上,看起来Graphics上下文由于某种原因在第一次绘制时没有被正确翻译,我不知道为什么……

ps-在Windows 7下对Java 6和Java 7进行测试
pps-我也尝试在屏幕可见之前将初始比例和旋转设置为其他值,结果相同

系统属性

 awt.toolkit=sun.awt.windows.WToolkit file.encoding=UTF-8 file.encoding.pkg=sun.io file.separator=\ java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment java.awt.printerjob=sun.awt.windows.WPrinterJob java.class.path=C:\DevWork\personal\java\projects\wip\SystemProperties\build\classes java.class.version=51.0 java.endorsed.dirs=C:\Program Files\Java\jdk1.7.0_51\jre\lib\endorsed java.ext.dirs=C:\Program Files\Java\jdk1.7.0_51\jre\lib\ext;C:\Windows\Sun\Java\lib\ext java.home=C:\Program Files\Java\jdk1.7.0_51\jre java.io.tmpdir=C:\Users\shane\AppData\Local\Temp\ java.runtime.name=Java(TM) SE Runtime Environment java.runtime.version=1.7.0_51-b13 java.specification.name=Java Platform API Specification java.specification.vendor=Oracle Corporation java.specification.version=1.7 java.vendor=Oracle Corporation java.vendor.url=http://java.oracle.com/ java.vendor.url.bug=http://bugreport.sun.com/bugreport/ java.version=1.7.0_51 java.vm.info=mixed mode java.vm.name=Java HotSpot(TM) 64-Bit Server VM java.vm.specification.name=Java Virtual Machine Specification java.vm.specification.vendor=Oracle Corporation java.vm.specification.version=1.7 java.vm.vendor=Oracle Corporation java.vm.version=24.51-b03 os.arch=amd64 os.name=Windows 7 os.version=6.1 path.separator=; sun.arch.data.model=64 sun.cpu.endian=little sun.cpu.isalist=amd64 sun.desktop=windows sun.io.unicode.encoding=UnicodeLittle sun.java.command=systemproperties.SystemProperties sun.java.launcher=SUN_STANDARD sun.jnu.encoding=Cp1252 sun.management.compiler=HotSpot 64-Bit Tiered Compilers sun.os.patch.level=Service Pack 1 user.country=AU user.dir=C:\DevWork\personal\java\projects\wip\SystemProperties user.home=C:\Users\shane user.language=en user.name=shane user.script= user.timezone= user.variant= 

更新

如果我用…

 g2d.translate(originX, originY); g2d.scale(scale, scale); g2d.rotate(Math.toRadians(angle), 0, 0); 

而不是AffineTransform ,它工作正常。 我注意到我使用AffineTransform (仅翻译,仅旋转,仅缩放)并不重要我似乎得到了相同的结果

更新了示例图像

显示调整框架的示例…

ZoomSpinResizeFrame

nb矩形的最后一个位置移位是下面提到的MouseListener的结果

但是,如果我将一个MosueListener添加到DrawPane (在类中的direclty或通过myPanel实例外部)并在mouseClicked上调用repaint ,它会正确地重新对齐:P

更新

如果我翻译一个Shape ,请使用以下内容

 AffineTransform at = new AffineTransform(); at.translate(originX, originY); at.scale(scale, scale); at.rotate(Math.toRadians(angle), 0, 0); g2d.setTransform(at); Rectangle2D rect = new Rectangle2D.Double(xOffset, yOffset, rectWidth, rectHeight); Shape shape = at.createTransformedShape(rect); System.out.println(rect.getBounds()); System.out.println(shape.getBounds()); 

结果输出符合预期,但(图形)输出仍然是错误的…

与传递给paintComponent()的图形上下文关联的AffineTransform并不总是身份转换。 由于不明确的原因, m12条目最初的值为38.0并且在调整封闭框架的大小之后。 通常,可以修改g.create()提供的副本。

 Graphics2D g2d = (Graphics2D) g.create(); AffineTransform at = g2d.getTransform(); at.translate(originX, originY); g2d.setTransform(at); g2d.scale(scale, scale); g2d.rotate(Math.toRadians(angle), 0, 0); g2d.fillRect(xOffset, yOffset, rectWidth, rectHeight); g2d.dispose(); 

附录:正如@MadProgrammer观察到的那样,“如果我从GUI中删除控件,它就会出现……在中心。” 实际上,观察到的水平偏移恰好是BorderLayout.WEST slider的首选宽度。 我怀疑在resize后调整原点以更容易地满足Component#paintAll()契约:“图形上下文的原点,它的(0, 0)坐标点,是该组件的左上角。”