Живопись и графика в целом являются довольно сложными темами, Java / Swing делает хорошую работу по «объединению» API-интерфейсов в нечто простое в использовании, но все же требует времени и усилий для изучения и полного понимания.
Я бы настоятельно рекомендовал иметь Выполнение пользовательской рисования , Рисование в AWT и Swing и 2D-графика и отмеченные JavaDocs, как вы вернетесь им регулярно (я все еще делаю)
Существует множество проблем, которые усложняют вашу жизнь.
Начиная с ...
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D)g;
paintRoulette(g2d);
}
Вы должны предпочесть переопределение paintComponent
вместо paint
, рисование - сложный процесс, и вам нужно тщательно выбирать точку входа. Кроме того, вы всегда должны вызывать методы рисования super
, если только вы не абсолютно уверены в том, что можете самостоятельно взять на себя его основные функции.
В вашем случае вы также должны сделать копию контекста Graphics
, прежде чем передавать его в paintRoulette
, так как Graphics
является общим ресурсом, и применяемые вами преобразования вызовут проблемы для всего, что раскрашивается после вашего компонента.
Трансформации ...
AffineTransform at = AffineTransform.getTranslateInstance(10, 10);
at.rotate(spinValue, 10, 10);
Это несколько интересно. Вы создаете перевод 10x10
, который будет перемещать исходную точку контекста Graphics
. Затем вы применяете вращение, привязанное к 10x10
.
Причина, по которой я это упоминаю, в том, что вы потом делаете ...
g2d.fill(new Arc2D.Double(100, 100, 300, 300, startAngle, angle, Arc2D.PIE));
Это означает, что дуга смещена на 110x110
от угла компонента (добавьте в ваш перевод), и вы будете вращаться вокруг точки 20x20
от верхнего / левого угла компонента (добавьте в свой перевод) ) ... это странно для меня, потому что центр колеса на самом деле находится в 250x250
(из верхнего / левого угла компонента), что может вызвать один очень странный эффект.
Наконец, вы применяете преобразование ПОСЛЕ завершения рисования И затем создаете Timer
внутри метода рисования ...
Картина выполнена в сериале. Таким образом, одна операция будет влиять на следующую, это будет означать, что вам нужно будет применить преобразование ПЕРЕД тем, как вы что-то рисуете (что вы хотите преобразовать)
Вам также необходимо понимать, что вы не контролируете процесс рисования, это означает, что ваш компонент может быть окрашен по любой причине в любое время без вашего взаимодействия. Это означает, что вы могли бы бесконечное число Timer
с за очень маленький промежуток времени.
Вместо этого ваш таймер должен управляться извне из paint
процесса.
Еще одна вещь, которая заняла у меня некоторое время, чтобы потренироваться - это ...
public int spinValue = 0;
//...
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
spinValue += 0.01;
repaint();
}
});
Вы объявляете spinValue
как int
, но добавляете к нему значение с плавающей запятой, это будет иметь эффект усечения десятичного компонента, поэтому значение ВСЕГДА будет 0
.
Кроме того, AffineTransform#rotate
ожидает, что углы будут в радианах, а не в градусах. Не уверен, что это важно, но вы должны знать об этом.
Пример запуска ...
Хорошо, после применения вышесказанного код "может" выглядеть примерно так ...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new RoulettePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RoulettePane extends JPanel {
private double spinValue = 0;
private Timer timer;
public RoulettePane() {
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
spin();
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
paintRoulette(g2d);
g2d.dispose();
}
protected void spin() {
if (timer != null && timer.isRunning()) {
return;
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
spinValue += 0.01;
repaint();
}
});
timer.start();
}
protected void paintRoulette(Graphics2D g2d) {
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(hints);
int width = getWidth();
int height = getHeight();
int dimeter = Math.min(width, height);
AffineTransform at = AffineTransform.getRotateInstance(spinValue, dimeter / 2, dimeter / 2);
g2d.transform(at);
double angle = 360 / 36.9;
double startAngle = 0;
int color = 0;
for (int i = 0; i < 37; i++) {
if (i == 0) {
g2d.setColor(Color.GREEN);
} else {
if (color == 0) {
g2d.setColor(Color.BLACK);
color = 1;
} else {
g2d.setColor(Color.RED);
color = 0;
}
}
g2d.fill(new Arc2D.Double(0, 0, dimeter, dimeter, startAngle, angle, Arc2D.PIE));
startAngle += angle;
}
}
}
}
nb: Я вывел перевод за то время, которое было раньше, поскольку я хотел сосредоточиться на том, чтобы сделать вывод более динамичным на основе фактической ширины / высоты компонента