问题
println in the paintComponent prints out 497 971. in the view of JPanel, the red line's left-up point is supposed to be somewhere near JPanel's middle according that number pair, but in fact it's not. Is it caused by coordinate system conversion?
Thanks in advance.
Code shows below:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ClockFrame extends JFrame {
JPanel panel;
public ClockFrame(){
panel = new JPanel();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
setSize(1000, 1000);
panel.setSize(getWidth(), getHeight());
//panel.setLayout(null);//!important
panel.setLayout(new GridLayout());
setVisible(true);
setResizable(false);
panel.setBackground(Color.BLACK);
Hand sHand=new Hand(panel);
panel.add(sHand);
}
class Hand extends JComponent{
private Timer timer;
public Hand(Object o){
setLocation(500,500);
((JPanel)o).add(this);
timer = new Timer(800, new ActionListener() {
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);System.out.println(panel.getWidth()/2 +" "+panel.getHeight());
g2d.drawLine(panel.getWidth()/2, panel.getHeight()/2, 30, 30);
g2d.dispose();
}
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
}
public static void main(String[] a) {
ClockFrame c=new ClockFrame();
}
}
回答1:
g2d.drawLine(0, 0, 100, 50);
g2d.setColor(Color.WHITE); // no good setting the color now!
Should be:
g2d.setColor(Color.WHITE); // correct
g2d.drawLine(0, 0, 100, 50);
Other tips:
Better to
pack()
the frame after all components are added. Then it will be the exact right size to display them. Setting a top-level container visible should be the last thing in the constructor (in the vast majority of cases).setSize(1000, 1000);
The location of the component is best determined by the layout manager, padding and borders.
public Hand(Object o){ setLocation(500,500);
If the component needs to be added to a container, better to pass it as a container. Having said that, probably best not to pass the container in the constructor at all, but instead to
add(..)
it in the code immediately after where it is instantiated.public Hand(Object o){ // .. ((JPanel)o).add(this);
Some of those concepts implemented in the code below. Be sure to run it to see the effect.
Note especially:
/* Note that this is translating further drawing operations
to the middle of the Hand container based on its preferred size,
which is (layout manager not withstanding) also its actual size.
All co-ordinates in custom painting are relative to the component
being painted. */
g2d.translate(middle, middle);
Code
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.util.Calendar;
import java.util.Date;
import javax.swing.*;
public class ClockFrame extends JFrame {
public ClockFrame() {
JPanel panel = new JPanel();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
panel.setSize(getWidth(), getHeight());
//panel.setLayout(null);//!important
panel.setLayout(new GridLayout());
setResizable(false);
panel.setBackground(Color.BLACK);
Hand sHand = new Hand(panel);
panel.add(sHand);
pack();
setVisible(true);
}
class Hand extends JComponent {
private Timer timer;
private Dimension preferredSize = new Dimension(600, 600);
private Calendar currentTime;
public Hand(Object o) {
setLocation(500, 500);
((JPanel) o).add(this);
currentTime = Calendar.getInstance();
timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
currentTime.setTime(new Date(System.currentTimeMillis()));
repaint();
}
});
timer.start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("x: " + this.getX() + " y: " + this.getY() + " w: " + this.getWidth() + " h: " + this.getHeight());
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.WHITE);
double angle = (currentTime.get(Calendar.SECOND)*2*Math.PI)/60d;
double middle = preferredSize.getWidth() / 2d;
/* Note that this is translating further drawing operations
to the middle of the Hand container based on its preferred size,
which is (layout manager not withstanding) also its actual size.
All co-ordinates in custom painting are relative to the component
being painted. */
g2d.translate(middle, middle);
Line2D.Double secondHand = new Line2D.Double(0, 0,
middle*.9*Math.cos(angle),
middle*.9*Math.sin(angle));
g2d.draw(secondHand);
g2d.dispose();
}
public Dimension getPreferredSize() {
return preferredSize;
}
}
public static void main(String[] a) {
ClockFrame c = new ClockFrame();
}
}
Edit: to include minute and hour hands
I find that after second hand if I add minute hand, my panel will be twice wide - I think it's because graphics cannot be superimposed...- and what I get is that the two graphics2D are separated far away from each other and repaint themselves..any good ideas to resolve this?
Because I was bored, I played around with the code a little more. I discovered the angle was off, so put an offset to correct it. Then I added minute and hour hands to that same custom component. I think the latter is the cause of the problem you describe (if not, show your latest code - though perhaps in a new question).
Try this version (note that second, minute and hour hands are all quantized to their respective time units):
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import java.io.IOException;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
public class ClockFrame extends JFrame {
public ClockFrame() {
JPanel panel = new JPanel();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(panel);
panel.setSize(getWidth(), getHeight());
panel.setLayout(new GridLayout());
setResizable(false);
panel.setBackground(Color.MAGENTA.darker().darker());
Hand sHand = new Hand(panel);
panel.add(sHand);
pack();
setVisible(true);
}
class Hand extends JComponent {
private Timer timer;
private Dimension preferredSize = new Dimension(600, 600);
private Calendar currentTime;
private Image clockFace;
public Hand(Object o) {
setLocation(500, 500);
((JPanel) o).add(this);
currentTime = Calendar.getInstance();
try {
clockFace = ImageIO.read(new URL(
"http://www.clipartbest.com/cliparts/LTK/kBp/LTKkBpRGc.png"));
} catch (IOException ex) {
Logger.getLogger(ClockFrame.class.getName()).log(Level.SEVERE, null, ex);
}
timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
currentTime.setTime(new Date(System.currentTimeMillis()));
repaint();
}
});
timer.start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(
RenderingHints.KEY_DITHERING,
RenderingHints.VALUE_DITHER_ENABLE);
g2d.setColor(Color.LIGHT_GRAY);
int size = preferredSize.width;
g2d.fillOval((int)(size*.01), (int)(size*.01), (int)(size*.98), (int)(size*.98));
if (clockFace!=null) {
g2d.drawImage(clockFace, 0, 0, this);
}
double middle = size / 2d;
/* Note that this is translating further drawing operations
to the middle of the Hand container based on its preferred size,
which is (layout manager not withstanding) also its actual size.
All co-ordinates in custom painting are relative to the component
being painted. */
g2d.translate(middle, middle);
g2d.setColor(Color.CYAN.darker().darker());
double angleHour = ((currentTime.get(Calendar.HOUR)*2*Math.PI)/12d)-(Math.PI/2);
g2d.setStroke(new BasicStroke(6.5f));
Line2D.Double hourHand = new Line2D.Double(0, 0,
middle*.83*Math.cos(angleHour),
middle*.83*Math.sin(angleHour));
g2d.draw(hourHand);
g2d.setColor(Color.CYAN.darker());
double angleMin = ((currentTime.get(Calendar.MINUTE)*2*Math.PI)/60d)-(Math.PI/2);
g2d.setStroke(new BasicStroke(4.5f));
Line2D.Double minuteHand = new Line2D.Double(0, 0,
middle*.85*Math.cos(angleMin),
middle*.85*Math.sin(angleMin));
g2d.draw(minuteHand);
g2d.setColor(Color.CYAN);
double angleSec = ((currentTime.get(Calendar.SECOND)*2*Math.PI)/60d)-(Math.PI/2);
g2d.setStroke(new BasicStroke(2.5f));
Line2D.Double secondHand = new Line2D.Double(0, 0,
middle*.87*Math.cos(angleSec),
middle*.87*Math.sin(angleSec));
g2d.draw(secondHand);
g2d.dispose();
}
public Dimension getPreferredSize() {
return preferredSize;
}
}
public static void main(String[] a) {
ClockFrame c = new ClockFrame();
}
}
来源:https://stackoverflow.com/questions/42266729/coordinate-system-of-jcomponent-relative-to-jpanel