Reputation: 1
I want to have a circle which works like a second-hand of a clock but I have a text field instead of a clock handle.for example when the second-hand of the clock is 30 the text field is in the lowest place of the circle and its text is 30.But I cant divide the circle into 60 parts.please help me to edit my codes. I have to mention that I'm using Timer class. these are my codes:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClockWiseRotation window = new ClockWiseRotation();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public ClockWiseRotation() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
second = Integer.toString(Integer.parseInt(handle.getText()) + 1);
for (int i = 33; i <= 60; i++) {
int angle = i * (360 / 60);
handle.setBounds((int)(202 + 100 * Math.cos(angle)),
(int)(119 + 100 * Math.sin(angle)), 23, 26);
}
handle.setText(second);
}
});
timer.start();
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
handle = new JTextField();
handle.setText("0");
handle.setBounds(202, 22, 23, 26);
frame.getContentPane().add(handle);
handle.setColumns(10);
JComboBox centerPoint = new JComboBox();
centerPoint.setBounds(202, 119, 17, 20);
frame.getContentPane().add(centerPoint);
}
}
Upvotes: 0
Views: 1025
Reputation: 1
Thanks your guidance was really helpful and i have solved it like this(by removing the for loop):
public void actionPerformed(ActionEvent arg0) {
int intSecond = Integer.parseInt(handle.getText()) + 1;
if (intSecond <= 60) {
second = Integer.toString(intSecond);
handle.setBounds(
(int) (Math.cos(intSecond * Math.PI / 30 - Math.PI
/ 2) * 100 + 202),
(int) (Math.sin(intSecond * Math.PI / 30 - Math.PI
/ 2) * 100 + 119), 17, 20);
handle.setText(second);
} else {
timer.stop();
}
}
});
Upvotes: 0
Reputation: 347314
Swing is a single threaded environment, blocking the EDT for any reason (like a long running loop of Thread.sleep
) will prevent the UI from been updated. Swing is also not thread and updates to the UI should never be made from outside the context of the EDT
With that in mind, on every tick of the Timer
, you looping 60 some 30 times trying to move the handle, but the only position the handle will appear in is the last position it's in when the actionPerformed
method exits.
See Concurrency in Swing for more details.
What you should be doing is simply placing the handle at the current location based on your requirements/needs at the time of the call
I've also been looking at you calculation...
handle.setBounds((int)(202 + 100 * Math.cos(angle)),
(int)(119 + 100 * Math.sin(angle)), 23, 26);
From what I remember, to find the point on a circle, based on an angle, you need to use...
x = xOffset + Math.cos(radians) * radius;
y = yOffset - Math.sin(radians) * radius;
So, two things to point out there, but the most important is Math.cos
and Math.sin
expect the value to be in radians, not degrees
You should also avoid using null
layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify.
You should also avoid using components in this manner, instead, I'd consider using a custom painting processing....
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestClock {
public static void main(String[] args) {
new TestClock();
}
public TestClock() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ClockPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
protected class ClockPane extends JPanel {
public ClockPane() {
Timer timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(false);
timer.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Point getPointTo(float angle) {
int x = Math.round(getWidth() / 2);
int y = Math.round(getHeight() / 2);
double rads = Math.toRadians(angle);
// This is an arbitrary amount, you will need to correct for this
// I'm working of a width of 200 pixels, so that makes the radius
// 100...
int radius = 100;
// Calculate the outter point of the line
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
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
Calendar cal = Calendar.getInstance();
int seconds = cal.get(Calendar.SECOND);
float angle = -(360f * (seconds / 60f));
angle += 90; // Correct for 0 being out to the right instead of up
Point p = getPointTo(angle);
int x = getWidth() / 2;
int y = getHeight() / 2;
g2d.drawLine(x, y, p.x, p.y);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(seconds);
g2d.drawString(text, getWidth() - fm.stringWidth(text), getHeight() - fm.getHeight() + fm.getAscent());
g2d.dispose();
}
}
}
Have a look at Painting in AWT and Swing, Performing Custom Painting and 2D Graphics for more details
Upvotes: 3