问题
I am trying to make an animation with multiple thread. I want to paint n squares where this n comes from commend-line argument. Every square has their x-y coordinates, colors and speed. They are moving to the right of the frame with different speed, color and coordinates. Since I am using multi thread I assume I have to control each squares. So I have to store each square object in the ArrayList. However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show. Here what I have done so far:
DrawSquare.java
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
@Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
@Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
Square.java
import java.awt.Color;
import java.util.Random;
public class Square {
private int x,y,r,s;
private Color c;
private Random random;
public Square() {
random = new Random();
x = random.nextInt(100) + 30;
y = random.nextInt(100) + 30;
r = random.nextInt(50) + 20;
s = random.nextInt(20) + 5;
c = new Color(random.nextInt(255),random.nextInt(255),random.nextInt(255));
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getR() {
return r;
}
public int getS() {
return s;
}
public Color getC() {
return c;
}
}
Animation.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Animation extends JFrame implements Runnable {
private JPanel panel;
private DrawSquare square;
public Animation() {
}
public static void main(String[] args) {
Animation w = new Animation();
DrawSquare square = new DrawSquare();
JFrame f = new JFrame("Week 9");
int n = Integer.parseInt(args[0]);
f.setVisible(true);
f.setSize(700,700);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setResizable(false);
for(int i=0; i<n; i++) {
f.getContentPane().add(square);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
}
}
回答1:
So, starting with...
public class DrawSquare extends JPanel {
public Square square;
public DrawSquare() {
square = new Square();
}
@Override
public void paintComponents(Graphics g) {
// TODO Auto-generated method stub
super.paintComponents(g);
}
@Override
public void paint(Graphics g) {
// TODO Auto-generated method stub
super.paint(g);
g.setColor(square.getC());
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
}
}
As general recommendation, it's preferred to put custom painting in the paintComponent
method (note, there's no s
at the end)
When paint
is called, the Graphics
context has already been translated to the component coordinate position. This means that 0x0
is the top/left corner of the component, this also means that...
g.fillRect(square.getX(), square.getY(), square.getR(), square.getR());
is painting the rect at x + x
x y + y
, which will, at the very least, paint the rect in the wrong position, at worst paint it beyond the visible scope of the component.
You're also not providing any sizing hints for the component, so it's default size will be 0x0
, which prevent it from been painted.
Since I am using multi thread I assume I have to control each squares.
Well, since I can't really see what's driving the animation, I imagine that when you say "multi thread" you're suggesting that each square has it's own `Thread. In this case, that's a bad idea. Let's put aside the thread synchronisation issues for a moment, more threads doesn't equate to more work you can do, at some point, it will begin to degrade the system performance.
In most cases, a single, well managed thread, is all you really need. You also have to understand that Swing is NOT thread safe. This means that you shouldn't update the UI (or states that the UI relies on) from outside the context of the Event Dispatching Thread.
So, why you're thread can update the position of the rects, you need to take care to ensure that they are not been painted why they are been update. Once you've updated the state, you then need to trigger a paint pass (which is trivial in of itself)
So I have to store each square object in the ArrayList.
Yep, good start
However, I am having trouble with painting those squares. I can paint one square but when I try to paint multiple squares, it does not show.
Okay, so instead of using multiple components, use one. Run through your ArrayList
within the paintComponent
method of this component and paint all the rects to it. This provides a much simpler way to manage things like bounds detection, as you have only one container to worry about.
I'd highly recommend you have a look at:
- Java Bouncing Ball which demonstrates many of the concepts discussed here
- Concurrency in Swing
- How to use Swing Timers
- Performing Custom Painting
- Painting in AWT and Swing
来源:https://stackoverflow.com/questions/47461863/java-drawing-multiple-squares-in-same-jframe