JComponent 相对于 JPanel 的坐标系 [英] coordinate system of JComponent relative to JPanel

查看:29
本文介绍了JComponent 相对于 JPanel 的坐标系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

paintComponent 中的 println 打印出 497 971.在 JPanel 看来,根据那个数字对,红线的左上点应该在 JPanel 中间附近的某处,但实际上并非如此. 是不是坐标系转换引起的?

提前致谢.

代码如下:

import java.awt.*;导入 java.awt.event.ActionEvent;导入 java.awt.event.ActionListener;导入 java.awt.image.BufferedImage;导入 java.io.File;导入 java.io.IOException;导入 java.util.Calendar;导入 javax.imageio.ImageIO;导入 javax.swing.*;公共类 ClockFrame 扩展 JFrame {JPanel面板;公共时钟帧(){面板 = 新 JPanel();setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);添加(面板);setSize(1000, 1000);panel.setSize(getWidth(), getHeight());//panel.setLayout(null);//!importantpanel.setLayout(new GridLayout());设置可见(真);setResizable(false);panel.setBackground(Color.BLACK);手 sHand=新手(面板);panel.add(sHand);}类 Hand 扩展了 JComponent{私人定时器定时器;公共手(对象 o){设置位置(500,500);((JPanel)o).add(this);计时器 = 新计时器(800,新的 ActionListener(){public void actionPerformed(ActionEvent e) {重绘();}});定时器开始();}受保护的无效paintComponent(图形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();}公共维度 getPreferredSize() {返回新维度(600, 600);}}public static void main(String[] a) {时钟帧 c=new ClockFrame();}}

解决方案

 g2d.drawLine(0, 0, 100, 50);g2d.setColor(Color.WHITE);//现在设置颜色不好!

应该是:

 g2d.setColor(Color.WHITE);//正确的g2d.drawLine(0, 0, 100, 50);

其他提示:

  1. 最好在添加所有组件后pack()框架.然后它将是显示它们的正确大小.设置顶级容器可见应该是构造函数中最后的事情(在绝大多数情况下).

    setSize(1000, 1000);

  2. 组件的位置最好由布局管理器、内边距和边框确定.

    public Hand(Object o){设置位置(500,500);

  3. 如果组件需要添加到容器中,最好将其作为容器传递.话虽如此,最好不要在构造函数中传递容器,而是在实例化后立即在代码中add(..).

    public Hand(Object o){//..((JPanel)o).add(this);

其中一些概念在下面的代码中实现.一定要运行看看效果.

特别注意:

/* 注意,这是在翻译进一步的绘图操作根据其首选大小到 Hand 容器的中间,这也是它的实际大小(不包括布局管理器).自定义绘画中的所有坐标都是相对于组件的被画.*/g2d.translate(中间,中间);

代码

import java.awt.*;导入 java.awt.event.*;导入 java.awt.geom.Line2D;导入 java.util.Calendar;导入 java.util.Date;导入 javax.swing.*;公共类 ClockFrame 扩展 JFrame {公共时钟帧(){JPanel 面板 = new JPanel();setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);添加(面板);panel.setSize(getWidth(), getHeight());//panel.setLayout(null);//!importantpanel.setLayout(new GridLayout());setResizable(false);panel.setBackground(Color.BLACK);手 sHand = 新手(面板);panel.add(sHand);盒();设置可见(真);}class Hand 扩展了 JComponent {私人定时器定时器;私有维度首选大小 = 新维度 (600, 600);私人日历 currentTime;公共手(对象 o){setLocation(500, 500);((JPanel) o).add(this);currentTime = Calendar.getInstance();计时器 = 新计时器(50,新动作监听器(){public void actionPerformed(ActionEvent e) {currentTime.setTime(new Date(System.currentTimeMillis()));重绘();}});定时器开始();}受保护的无效paintComponent(图形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);双角 = (currentTime.get(Calendar.SECOND)*2*Math.PI)/60d;双中 = preferredSize.getWidth()/2d;/* 请注意,这是翻译进一步的绘图操作根据其首选大小到 Hand 容器的中间,这也是它的实际大小(不包括布局管理器).自定义绘画中的所有坐标都是相对于组件的被画.*/g2d.translate(中间,中间);Line2D.Double secondHand = new Line2D.Double(0, 0,中*.9*Math.cos(角度),中*.9*Math.sin(angle));g2d.draw(secondHand);g2d.dispose();}公共维度 getPreferredSize() {返回首选大小;}}public static void main(String[] a) {ClockFrame c = new ClockFrame();}}

包括分针和时针

<块引用>

我发现秒针后如果我加上分针,我的面板会变成两倍宽 - 我认为这是因为图形不能叠加...... - 我得到的是两个graphics2D彼此分开很远并重新粉刷自己..有什么好主意可以解决这个问题?

因为我很无聊,所以我又玩了一下代码.我发现角度不对,所以设置一个偏移来纠正它.然后我将分针和时针添加到相同的自定义组件.我认为后者是您描述的问题的原因(如果不是,请显示您的最新代码 - 尽管可能是在一个新问题中).

试试这个版本(注意秒针、分针和时针量化为各自的时间单位):

import java.awt.*;导入 java.awt.event.*;导入 java.awt.geom.Line2D;导入 java.io.IOException;导入 java.net.URL;导入 java.util.Calendar;导入 java.util.Date;导入 java.util.logging.Level;导入 java.util.logging.Logger;导入 javax.imageio.ImageIO;导入 javax.swing.*;公共类 ClockFrame 扩展 JFrame {公共时钟帧(){JPanel 面板 = new JPanel();setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);添加(面板);panel.setSize(getWidth(), getHeight());panel.setLayout(new GridLayout());setResizable(false);panel.setBackground(Color.MAGENTA.darker().darker());手 sHand = 新手(面板);panel.add(sHand);盒();设置可见(真);}class Hand 扩展了 JComponent {私人定时器定时器;私有维度首选大小 = 新维度 (600, 600);私人日历 currentTime;私人图像时钟;公共手(对象 o){setLocation(500, 500);((JPanel) o).add(this);currentTime = Calendar.getInstance();尝试 {clockFace = ImageIO.read(新网址("http://www.clipartbest.com/cliparts/LTK/kBp/LTKkBpRGc.png"));} catch (IOException ex) {Logger.getLogger(ClockFrame.class.getName()).log(Level.SEVERE, null, ex);}计时器 = 新计时器(50,新动作监听器(){public void actionPerformed(ActionEvent e) {currentTime.setTime(new Date(System.currentTimeMillis()));重绘();}});定时器开始();}受保护的无效paintComponent(图形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));如果(钟面!=空){g2d.drawImage(clockFace, 0, 0, this);}双中 = 大小/2d;/* 请注意,这是翻译进一步的绘图操作根据其首选大小到 Hand 容器的中间,这也是它的实际大小(不包括布局管理器).自定义绘画中的所有坐标都是相对于组件的被画.*/g2d.translate(中间,中间);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,中*.83*Math.cos(angleHour),中*.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,中*.85*Math.cos(angleMin),中*.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,中*.87*Math.cos(angleSec),中*.87*Math.sin(angleSec));g2d.draw(secondHand);g2d.dispose();}公共维度 getPreferredSize() {返回首选大小;}}public static void main(String[] a) {ClockFrame c = new ClockFrame();}}

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();
  }
}

解决方案

        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:

  1. 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);
    

  2. The location of the component is best determined by the layout manager, padding and borders.

    public Hand(Object o){
        setLocation(500,500);
    

  3. 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();
    }
}

这篇关于JComponent 相对于 JPanel 的坐标系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆