I have a problem. I have to make an app, which would contain n
number of Threads which would move the balls around the circle (with different velocities). Now I've got my Thread extending JComponent
and implementing Runnable
. Everything works just fine, except the run()
method doesn't seem to call paintComponent. How can I fix that? The code:
Thread (Kulka.java):
package internet;
import internet.RotateWheel.TestPane;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import static java.lang.Thread.sleep;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class Kulka extends JComponent implements Runnable{
int x, y;
float predkosc;
float obecna_predkosc = 0f;
TestPane parent;
public Kulka(TestPane parent, int x, int y, float v){
this.predkosc = v;
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90);
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
@Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
int diameter = Math.min(getWidth(), getHeight());
int x = (getWidth() - diameter) / 2;
int y = (getHeight() - diameter) / 2;
g2.setColor(Color.RED);
float innerDiameter = 20;
System.out.println("Diameter: " + diameter);
Point p = getPointOnCircle(obecna_predkosc, (diameter / 2f) - (innerDiameter / 2));
g2.fillOval(x + p.x - (int) (innerDiameter / 2), y + p.y - (int) (innerDiameter / 2), (int) innerDiameter, (int) innerDiameter);
}
public synchronized void run(){
while (true){
//System.out.println(obecna_predkosc);
try {
System.out.println(predkosc);
this.repaint();
sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
This is what happens:
Class with Frame and Panel:
package internet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateWheel {
public static void main(String[] args) {
new RotateWheel();
}
public RotateWheel() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
LinkedList<Thread> kulki = new LinkedList<Thread>();
int n = 5;
private float degrees = 0;
Random random = new Random();
public TestPane() {
/*Timer timer = new Timer(60, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
degrees += 1f;
repaint();
}
});
timer.start();*/
}
public Point getPointOnCircle(float degress, float radius) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(degress - 90);
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int diameter = Math.min(getWidth(), getHeight());
int x = (getWidth() - diameter) / 2;
int y = (getHeight() - diameter) / 2;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, diameter, diameter);
g2d.setColor(Color.RED);
float innerDiameter = 20;
if (kulki.isEmpty()){
for (int i=0; i<n; i++){
Point p = getPointOnCircle(degrees, (diameter / 2f) - (innerDiameter / 2));
Kulka kulka = new Kulka(this, p.x, p.y, random.nextFloat() * (5f - 2f) + 1f);
Thread watek = new Thread(kulka);
kulki.add(watek);
}
for (Thread t: kulki){
t.start();
}
}
System.out.println("No elo XD");
g2d.dispose();
}
}
}
Let's look at the obvious issues
TestPane
, you create a series of Kulka
components, but never add them to anything paintComponent
method Kulka
to be any type of component What are the possible solutions?
Kulka
elements within the constructor of the class or some other method unrelated to the UI Kulka
could be just a POJO, which has some means to paint to a Graphics
context and update itself as required. Technically, this could be self contained, but there's nothing wrong with having it rely on an external update source This is a pretty basic class, it's reliant on an external updater and painter, which makes it flexible. This means you could use multiple threads, a single thread or a Timer
to act as the updater and it won't care. It also doesn't care about how it's painted, only that it requires a Graphics
context to do it.
public class Kulka {
private PaintContainer container;
private float angle;
private float delta;
public Kulka(PaintContainer container, float angle, float delta) {
this.container = container;
this.angle = angle;
this.delta = delta;
}
public void update() {
angle += delta;
container.wasUpdated(this);
}
public void paint(Graphics2D g2d) {
int diameter = Math.min(container.getWidth(), container.getHeight());
float innerDiameter = 20;
Point point = getPointOnCircle(angle, (diameter / 2f) - (innerDiameter / 2));
g2d.setColor(Color.RED);
g2d.fill(new Ellipse2D.Double(point.x, point.y, innerDiameter, innerDiameter));
}
public Point getPointOnCircle(float degress, float radius) {
int x = Math.round(container.getWidth() / 2);
int y = Math.round(container.getHeight() / 2);
double rads = Math.toRadians(degress - 90);
int xPosy = Math.round((float) (x + Math.cos(rads) * radius));
int yPosy = Math.round((float) (y + Math.sin(rads) * radius));
return new Point(xPosy, yPosy);
}
}
Because I'm a crusty old developer, I prefer to use interfaces where I can, but strictly speaking, it's not required. I've used it here to force the separation and prevent the Kulka
class from interacting with the painting container in ways I don't want it to.
public interface PaintContainer {
public int getWidth();
public int getHeight();
public void wasUpdated(Kulka kulka);
}
Okay, so based on your requirements, you need a single thread for each Kulka
, as I've noted, this is a bad idea and won't scale well, but this is what you require.
This simply acts as a wrapper around the Kulka
and the Thread
. Now, it would be possible for Kulka
to be self contained and update itself, but I thought it'd be better if Kulka
had no concept of how it was updated, only that it can.
public class Updater implements Runnable {
private Kulka kulka;
private Thread t;
public Updater(Kulka kulka) {
this.kulka = kulka;
}
public void start() {
if (t == null) {
t = new Thread(this);
t.start();
}
}
@Override
public void run() {
while (true) {
kulka.update();
try {
Thread.sleep(16);
} catch (InterruptedException ex) {
}
}
}
}
I did think about trying to use a "smart" painting process, only updating those elements which have changed, but the fact is, on each paint cycle, we need to repaint everything any way :P
public class TestPane extends JPanel implements PaintContainer {
private Set<Kulka> entities;
public TestPane() {
entities = new HashSet<>(10);
for (int index = 0; index < 10; index++) {
float degrees = (float)(Math.random() * 359d);
float delta = (float)((Math.random() * 10d) - 5d);
Kulka kulka = new Kulka(this, degrees, delta);
entities.add(kulka);
Updater updater = new Updater(kulka);
updater.start();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
synchronized protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int diameter = Math.min(getWidth(), getHeight());
int x = (getWidth() - diameter) / 2;
int y = (getHeight() - diameter) / 2;
g2d.setColor(Color.GREEN);
g2d.drawOval(x, y, diameter, diameter);
g2d.setColor(Color.RED);
g2d.dispose();
for (Kulka kulka : entities) {
g2d = (Graphics2D) g.create();
kulka.paint(g2d);
g2d.dispose();
}
}
@Override
synchronized public void wasUpdated(Kulka kulka) {
repaint();
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.