问题
I have been working on trying to make this program work, even though I can't seem to find whatever is the problem. This program is made of the following 2 classes, no more, no less. It is basically supposed to draw a point on each click in the drawing zone, and on the 3rd click all points are connected. I still have to work on making it prettier and more accurate, but this part works. What doesn't work is what should follow : on the fourth click a thread should start (and it starts), and the triangle itself should rotate given an arbitrary refresh rate, for exactly 80 repaints. The next click should not work until the animating is finished and only then if there is a click after the animation stops(the thread dies), a new point is displayed and it starts all over again.
Is it possible that all paint calls are stacked until the end of my thread? I know it can happen that all events are stacked in the event queue and treated as one. Using a repaint with a time parameter doesn't help. I have added comments to help clarify, because all variables are french words (they're explained). I have a hard time trying to figure out if it's in my code to find the problem, if it's thread-related or even type-related. I'm just going nowhere in debug mode (using Eclipse). Have I completely ignored something obvious?
While my methods might not be the most efficient way to do this, the rotation is my primary problem. I can handle writing the unwritten remaining part of the code. Thanks for any help! Here are the two classes :
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Application extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel contentPane;
private JButton btnTerminer;
private Triangle triangle;
private int totalClics = 0;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Application frame = new Application();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public Application() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 453, 692);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
btnTerminer = new JButton("Terminer");
btnTerminer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.exit(0);
}
});
btnTerminer.setBounds(138, 622, 132, 23);
contentPane.add(btnTerminer);
triangle = new Triangle();
//Adds points for every mouse click
triangle.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(totalClics < 3){
triangle.ajouterPoint(e.getX(), e.getY());
totalClics++;
} else {
triangle.getAnim().start();
totalClics = 0;
}
}
});
triangle.setBounds(10, 11, 400, 600);
contentPane.add(triangle);
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import javax.swing.JPanel;
public class Triangle extends JPanel implements Runnable,Serializable{
private static final long serialVersionUID = 1L;
private ArrayList<Point> points = null;
//Animation thread
private Thread anim;
private Color couleurPrin;
private Color couleurBoite;
//A point's diameter
private int diametre = 8;
//The rectangle's width
private int largeur;
//The rectangle's height
private int hauteur;
//The rectangle's top-left corner
private int minX;
private int minY;
//Angle incrementation multiplier
private int nbAng = 0 ;
//Thread stopping variable
private boolean continuer = true;
public Triangle() {
setPreferredSize(new Dimension(400, 600));
setBackground(Color.BLACK);
couleurPrin = Color.GREEN;
couleurBoite = Color.RED;
setAnim(new Thread(this));
points = new ArrayList<Point>();
}
/**
* Repaints this component
*/
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int i = 0;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int[] coorX = new int[points.size()+1];
int[] coorY = new int[points.size()+1];
Iterator<Point> iter = points.iterator();
while(iter.hasNext()){
Point p = iter.next();
coorX[i] = p.getX();
coorY[i]= p.getY();
i++;
}
coorX[points.size()] = coorX[0];
coorY[points.size()] = coorY[0];
if(points.size() != 0){
g2d.setColor(Color.white);
g2d.fillOval(minX+largeur/2, minY+hauteur/2, 6, 6);
g2d.setColor(couleurPrin);
for(i =0; i<points.size(); i++){
g2d.drawLine(coorX[i], coorY[i], coorX[i+1], coorY[i+1]);
}
for(i = 0; i<points.size(); i++){
g2d.fillOval(coorX[i]-diametre/2, coorY[i]-diametre/2, diametre, diametre);
}
g2d.setColor(couleurBoite);
g2d.drawRect(minX, minY, largeur, hauteur);
g2d.rotate(15.0*nbAng, (largeur+getWidth())/2, (hauteur+getWidth())/2);
}
}
/**
* Adds a point. Stops at 3.
* @param x
* @param y
*
*
*/
public void ajouterPoint(int x, int y){
Point p = new Point(x,y);
System.out.println(p.toString());
if(points.size()>=0 && points.size()<3){
points.add(p);
minX = p.getX()-3;
minY = p.getY()-3;
}
if(points.size() == 3){
rectanguler(points);
}
repaint();
}
public Color getCouleurPrin() {
return couleurPrin;
}
public void setCouleurPrin(Color c) {
this.couleurPrin = c;
repaint();
}
public int getDiametre() {
return diametre;
}
public void setDiametre(int d) {
this.diametre = d;
repaint();
}
/**
* Sets rectangle's values to the largest bounds possible
* @param points
*/
private void rectanguler(ArrayList<Point> points){
Iterator<Point> iter = points.iterator();
Point p1, p2, p3;
p1 = iter.next();
p2 = iter.next();
p3 = iter.next();
int dLarg;
int dLong;
if(p2 != null && p3 != null){
minX = Math.min(p1.getX(), p2.getX());
minY = Math.min(p1.getY(), p2.getY());
largeur = Math.abs(p1.getX()-p2.getX());
hauteur = Math.abs(p1.getY()-p2.getY());
if(p3 != null){
minX = Math.min(minX, p3.getX());
minY = Math.min(minY, p3.getY());
dLarg = Math.max(Math.abs(p3.getX()-p2.getX()), Math.abs(p3.getX()-p1.getX()));
dLong = Math.max(Math.abs(p3.getY()-p2.getY()), Math.abs(p3.getY()-p1.getY()));
largeur = Math.max(dLarg, largeur);
hauteur = Math.max(dLong, hauteur);
}
}
}
/**
* Custom point class
* Stores an x and y value
*
*/
private class Point{
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
private int x,y;
public Point(){
setX(0);
setY(0);
}
public Point(int x,int y){
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
/**
* Starts the rotation
*
*/
@Override
public void run() {
int i =1;
while(continuer){
nbAng = i;
repaint();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println("Erreur dans le thread");
e.printStackTrace();
}
i++;
if(i== 80){
continuer = false;
}
}
anim = new Thread(this);
}
public Thread getAnim() {
return anim;
}
public void setAnim(Thread anim) {
this.anim = anim;
repaint();
}
}
回答1:
The first problem is that you never call start()
on the animation thread.
The second problem is that you should never do gui stuff outside of the EDT. as mentioned by @AndrewThompson in his comment, you should be using a swing Timer instead of a Thread.
回答2:
A Thread
is overkill for what you want to achieve. A javax.swing.Timer
is simpler and easier to use, it is also re-usable, unlike a Thread
.
Graphics2D#rotate
applies a translation to all subsequent rendering, meaning you need to apply it BEFORE you paint anything.
Your mouse click logic is a little off as well, it doesn't check the state of the thread before allowing the clicks to continue.
I modified your code a little as an example (sorry, I moved the mouse click handling to the Triangle
class, but you should be able to remove it ;))
public class TestRotation01 {
public static void main(String[] args) {
new TestRotation01();
}
public TestRotation01() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Triangle());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Triangle extends JPanel {
private static final long serialVersionUID = 1L;
private ArrayList<Point> points = null;
//Animation thread
// private Thread anim;
private Color couleurPrin;
private Color couleurBoite;
//A point's diameter
private int diametre = 8;
//The rectangle's width
private int largeur;
//The rectangle's height
private int hauteur;
//The rectangle's top-left corner
private int minX;
private int minY;
//Angle incrementation multiplier
private int nbAng = 0;
//Thread stopping variable
private boolean continuer = true;
private int totalClics = 0;
private Timer timer;
private int cycle;
public Triangle() {
setPreferredSize(new Dimension(400, 600));
setBackground(Color.BLACK);
couleurPrin = Color.GREEN;
couleurBoite = Color.RED;
points = new ArrayList<Point>();
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (!timer.isRunning()) {
if (totalClics < 3) {
nbAng = 0;
ajouterPoint(e.getX(), e.getY());
totalClics++;
} else {
cycle = 0;
totalClics = 0;
timer.restart();
}
}
}
});
timer = new Timer(200, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
nbAng += 5;
repaint();
cycle++;
if (cycle == 80) {
timer.stop();
}
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
}
/**
* Repaints this component
*/
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (timer.isRunning()) {
g2d.rotate(15.0 * nbAng, (largeur + getWidth()) / 2, (hauteur + getWidth()) / 2);
}
int i = 0;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int[] coorX = new int[points.size() + 1];
int[] coorY = new int[points.size() + 1];
Iterator<Point> iter = points.iterator();
while (iter.hasNext()) {
Point p = iter.next();
coorX[i] = p.getX();
coorY[i] = p.getY();
i++;
}
coorX[points.size()] = coorX[0];
coorY[points.size()] = coorY[0];
if (points.size() != 0) {
g2d.setColor(Color.white);
g2d.fillOval(minX + largeur / 2, minY + hauteur / 2, 6, 6);
g2d.setColor(couleurPrin);
for (i = 0; i < points.size(); i++) {
g2d.drawLine(coorX[i], coorY[i], coorX[i + 1], coorY[i + 1]);
}
for (i = 0; i < points.size(); i++) {
g2d.fillOval(coorX[i] - diametre / 2, coorY[i] - diametre / 2, diametre, diametre);
}
g2d.setColor(couleurBoite);
g2d.drawRect(minX, minY, largeur, hauteur);
}
g2d.dispose();
}
/**
* Adds a point. Stops at 3.
*
* @param x
* @param y
*
*
*/
public void ajouterPoint(int x, int y) {
Point p = new Point(x, y);
System.out.println(p.toString());
if (points.size() >= 0 && points.size() < 3) {
points.add(p);
minX = p.getX() - 3;
minY = p.getY() - 3;
}
if (points.size() == 3) {
rectanguler(points);
}
repaint();
}
public Color getCouleurPrin() {
return couleurPrin;
}
public void setCouleurPrin(Color c) {
this.couleurPrin = c;
repaint();
}
public int getDiametre() {
return diametre;
}
public void setDiametre(int d) {
this.diametre = d;
repaint();
}
/**
* Sets rectangle's values to the largest bounds possible
*
* @param points
*/
private void rectanguler(ArrayList<Point> points) {
Iterator<Point> iter = points.iterator();
Point p1, p2, p3;
p1 = iter.next();
p2 = iter.next();
p3 = iter.next();
int dLarg;
int dLong;
if (p2 != null && p3 != null) {
minX = Math.min(p1.getX(), p2.getX());
minY = Math.min(p1.getY(), p2.getY());
largeur = Math.abs(p1.getX() - p2.getX());
hauteur = Math.abs(p1.getY() - p2.getY());
if (p3 != null) {
minX = Math.min(minX, p3.getX());
minY = Math.min(minY, p3.getY());
dLarg = Math.max(Math.abs(p3.getX() - p2.getX()), Math.abs(p3.getX() - p1.getX()));
dLong = Math.max(Math.abs(p3.getY() - p2.getY()), Math.abs(p3.getY() - p1.getY()));
largeur = Math.max(dLarg, largeur);
hauteur = Math.max(dLong, hauteur);
}
}
}
/**
* Custom point class Stores an x and y value
*
*/
private class Point {
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
private int x, y;
public Point() {
setX(0);
setY(0);
}
public Point(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
}
}
来源:https://stackoverflow.com/questions/13964419/rotation-transform-not-working-properly-in-repaint-when-running-thread