JComponent 相对于 JPanel 的坐标系
coordinate system of JComponent relative to JPanel
paintComponent 中的println 打印出497 971,在JPanel 看来,红线左上角的点应该在JPanel 中间附近的某处,根据那个数字对,但实际上不是。 是不是坐标系转换造成的?
提前致谢。
代码如下:
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();
}
}
g2d.drawLine(0, 0, 100, 50);
g2d.setColor(Color.WHITE); // no good setting the color now!
应该是:
g2d.setColor(Color.WHITE); // correct
g2d.drawLine(0, 0, 100, 50);
其他提示:
最好是pack()
添加所有组件后的框架。然后它将是显示它们的正确尺寸。设置一个 top-level 容器可见应该是构造函数中的 last 事情(在绝大多数情况下)。
setSize(1000, 1000);
组件的位置最好由布局管理器、填充和边框决定。
public Hand(Object o){
setLocation(500,500);
如果需要将组件添加到容器中,最好将其作为容器传递。话虽如此,最好不要在构造函数中传递容器,而是在实例化后立即在代码中 add(..)
它。
public Hand(Object o){
// ..
((JPanel)o).add(this);
下面的代码中实现了其中的一些概念。一定要运行看看效果
特别注意:
/* 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);
代码
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();
}
}
编辑:包括分针和时针
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?
因为无聊,我多玩了一下代码。我发现角度不对,所以用偏移量来修正它。然后我将分针和时针添加到 相同的自定义组件 。我认为后者是您描述的问题的原因(如果不是,请显示您的最新代码 - 尽管可能在一个新问题中)。
试试这个版本(注意秒针、分针和时针都全部量化为它们各自的时间单位):
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();
}
}
paintComponent 中的println 打印出497 971,在JPanel 看来,红线左上角的点应该在JPanel 中间附近的某处,根据那个数字对,但实际上不是。
提前致谢。
代码如下:
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();
}
}
g2d.drawLine(0, 0, 100, 50);
g2d.setColor(Color.WHITE); // no good setting the color now!
应该是:
g2d.setColor(Color.WHITE); // correct
g2d.drawLine(0, 0, 100, 50);
其他提示:
最好是
pack()
添加所有组件后的框架。然后它将是显示它们的正确尺寸。设置一个 top-level 容器可见应该是构造函数中的 last 事情(在绝大多数情况下)。setSize(1000, 1000);
组件的位置最好由布局管理器、填充和边框决定。
public Hand(Object o){ setLocation(500,500);
如果需要将组件添加到容器中,最好将其作为容器传递。话虽如此,最好不要在构造函数中传递容器,而是在实例化后立即在代码中
add(..)
它。public Hand(Object o){ // .. ((JPanel)o).add(this);
下面的代码中实现了其中的一些概念。一定要运行看看效果
特别注意:
/* 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);
代码
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();
}
}
编辑:包括分针和时针
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?
因为无聊,我多玩了一下代码。我发现角度不对,所以用偏移量来修正它。然后我将分针和时针添加到 相同的自定义组件 。我认为后者是您描述的问题的原因(如果不是,请显示您的最新代码 - 尽管可能在一个新问题中)。
试试这个版本(注意秒针、分针和时针都全部量化为它们各自的时间单位):
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();
}
}