Графический контекст, несогласованный на первой краске
Я уже отвечал за еще один вопрос и столкнулся с проблемой bizare, которую я раньше не видел...
В принципе, программа использует AffineTransform
для обеспечения перевода, масштабирования и поворота элемента Graphics
, достаточно простого материала, сделанного тысячу раз перед
Проблема заключается в том, что при первом появлении экрана выход не там, где он должен быть, но как только я касаюсь одного из элементов управления (настройте один из слайдов), он переместится в нужное место.
![Slider]()
![Slider]()
Основываясь на снимках экрана, содержимое Graphics
, кажется, неуместно на количество других элементов управления.
Если я удалю элементы управления из графического интерфейса, он появится в нужном месте (в центре). Если я изменил размер окна, это не исправит проблему, оно будет исправлено только тогда, когда один из ползунков запускает repaint
на DrawPane
...
Я добавил диагностику к выходу, и все значения совпадают, т.е. они печатают те же самые значения, когда программа сначала запускается, и когда я настраиваю все значения ползунка на их начальные значения.
Если я удалю вызовы setRotation
и setScale
из AffineTransform
, он не исправит его. Если я удалю setTranslation
, квадрат не будет первоначально нарисован до тех пор, пока панель не будет обновлена (она будет выкрашена на экран)
Если я использую Graphics2D g2d = (Graphics)g;
вместо g.create()
, тот же результат (и да, я reset преобразование перед тем, как метод 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 - Тест на Java 6 и Java 7 под Windows 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
(либо в classclty внутри класса, либо извне через экземпляр myPanel
) и вызовите repaint
на mouseClicked
, он снова выравнивается: 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());
Результирующий результат соответствует ожиданиям, но вывод (графика) по-прежнему ошибочен...
Ответы
Ответ 1
AffineTransform
, связанный с графическим контекстом, переданным в paintComponent()
, не всегда является преобразованием идентичности. По непонятным причинам запись 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: "Если я удалю элементы управления из графического интерфейса, он появится... в центре". Действительно, наблюдаемое горизонтальное смещение является именно предпочтительной шириной slider
в BorderLayout.WEST
. Я подозреваю, что происхождение корректируется после изменения размера, чтобы легче встретить Component#paintAll()
: "Происхождение графического контекста, его (0, 0)
координатная точка, это левый верхний угол этого компонента."